设*可以匹配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语言实现的带通配符匹配算法 示例代码中有些小瑕疵,在本文的例子中已修正
网友评论