設*可以匹配0~n個任意字符,?可以匹配一個任意字符,實現字符串含通配符的匹配算法,查找算法
public class WildChar {
static boolean isEmpty(final String str) {
return str == null || str.isEmpty();
}
// pattern may contain '*' and '?'
// 嚴格匹配,str的首尾必須和pattern的首尾匹配
static boolean isMatch(final String str, final String pattern) {
if (isEmpty(str) || isEmpty(pattern)) {
return false;
}
int i = 0;
int j = 0;
int mark = -1; // 最近一個*的下一個字符的位置
while (i < str.length() && j < pattern.length()) {
if (pattern.charAt(j) == '?') {
i++;
j++;
continue;
}
if (pattern.charAt(j) == '*') {
j++;
mark = j;
continue;
}
if (str.charAt(i) != pattern.charAt(j)) {
if (mark < 0) {
// pattern中沒有*,直接return false
return false;
}
// * 之后出現了不匹配的字符
// j 回退到mark, i 比j少回退一步, mark之前是已經匹配成功的,i比j少回退一步,是因為i如果和j回退的步數一樣,則相當于又重新從上一個*之后開始匹配,這已經比較過一遍,失配了,繼續重新從這里匹配就會死循環了,所以i少回退一步,繼續比較
i -= j - mark - 1;
j = mark;
continue;
}
i++;
j++;
}
if (j == pattern.length()) {
if (i == str.length()) {
return true;
}
if (pattern.charAt(j - 1) == '*') {
// str還有剩余字符,但pattern最后一個字符是*,匹配成功
return true;
}
return false;
}
while (j < pattern.length()) {
if (pattern.charAt(j) != '*') {
// pattern還有剩余字符,且字符中有非*字符,匹配失敗
return false;
}
j++;
}
return true;
}
// str may contain '?'
static int[] getNextArray(final String str) {
if (isEmpty(str)) {
return null;
}
int[] next = new int[str.length()];
int k = -1;
int j = 0;
next[0] = -1;
while (j < str.length() - 1) {
if (k == -1 || str.charAt(k) == str.charAt(j) || str.charAt(k) == '?' || str.charAt(j) == '?') {
k++;
j++;
next[j] = k;
} else {
k = next[k];
}
}
return next;
}
// pattern may contain '?'
static int kmpFind(final String str, final String pattern, int start) {
if (isEmpty(str)) {
return -1;
}
int[] next = getNextArray(pattern);
if (next == null) {
return -1;
}
int i = start;
while (i < str.length()) {
int j = 0;
while (j < pattern.length()) {
if (str.charAt(i) == pattern.charAt(j) || pattern.charAt(j) == '?') {
i++;
j++;
} else {
break;
}
}
i -= j;
if (j == pattern.length()) {
return i;
}
int move = j - next[j];
i += move;
}
return -1;
}
// pattern may contain '*' and '?'
// pattern按*分割后,子串里可能含有?,沒法用String.find, 所以針對含?的字符串,結合KMP算法,實現了find函數,之后再將pattern按*分割,在輸入字符串中按順序查找子串,已實現find含有*和?的字符串
static int find(final String str, final String pattern) {
if (isEmpty(str)) {
return -1;
}
if (isEmpty(pattern)) {
return -1;
}
String[] items = pattern.split("\\*");
int i = 0;
int ret = -1;
for (String s : items) {
int index = kmpFind(str, s, i);
if (index < 0) {
return -1;
}
if (i == 0) {
ret = index;
}
i = index + s.length();
}
return ret;
}
public static void main(String[] argv) {
//System.out.println(String.format("%s matching %s is %b", argv[0], argv[1], isMatch(argv[1], argv[0])));
//System.out.println(String.format("kmpFind %s in %s is %d", argv[0], argv[1], kmpFind(argv[1], argv[0])));
System.out.println(String.format("find %s in %s is %d", argv[0], argv[1], find(argv[1], argv[0])));
}
}
參考閱讀
c語言實現的帶通配符匹配算法 示例代碼中有些小瑕疵,在本文的例子中已修正