题目:查找字符串txt中是否有某个子串pat
解决方案:从右到左地扫描pat, 并启发式地处理不匹配的字符
步骤1:构建一个跳跃表字母表中每个字符在pat中出现的最右位置,该值提示了如果该字符在文本中且在查找时造成一次匹配失败,应该向右跳跃多远。
要将right[]数组初始化,所有元素为-1,然后对于0到m-1的j,将right[pat.charAt(j)]设为j;
步骤2:
用一个索引i在文本str中从左向右移动,用另一个索引j在模式pat中从右向左移动。内循环会检查正文和模式字符串在位置i是否一致,如果从M-1到0的所有j,
txt.chatAt(i+i)都和pat.charAt(j)相等,那么就找到一个匹配。否则匹配失败,就会出现三种情况:
1.如果匹配失败的字符不包含在pat中,将模式字符串向右移动j+1个位置(即将i增加j+1).小于这个偏移量只可能使该字符与模式的某个字符重叠,事实上,这次移动会将pat前面已知的字符和结尾的一部分已知字符对齐。通过预先计算一张类似KMP算法的表格,还可以将i值变得更大
2.如果造成匹配失败的字符在pat中,就可以用righ[]数组来将模式字符串和文本对齐,使得该字符与它在pat中出现的最右位置相匹配。和刚才一样,小于这个偏移量只可能使该字符和模式中的与它无法匹配的字符重叠。通过预先计算一张类似KMP算法的表格,还可以将i值变得更大
3.如果这种方式无法增大i,那就直接将i加1来保证pat字符串至少向右移动一个位置。
代码如下:
class Question1{
private String pat;
private int[] right;
public Question1(String pat){
//计算跳跃表
this.pat=pat;
int M=pat.length();
int R=256;
right=new int[R];
for(int c=0;c<R;c++)
right[c]=-1;
for(int j=0;j<M;j++)
right[pat.charAt(j)]=j;
}
public int findPosition(String str){
int skip=0;
int N=str.length();
int M=pat.length();
for(int i=0;i<=N-M;i+=skip){
skip=0;
for(int j=M-1;j>=0;j--){
if(pat.charAt(j)!=str.charAt(i+j)){
skip=j-right[str.charAt(i+j)];
if(skip<1) skip=1;
break;
}
}
if(skip==0) return i;
}
return N;
}
}
public class FindSubString {
public static void main(String[] args) {
Question1 question=new Question1("needle");
String str="findinahaystrackneedleina";
int pos=question.findPosition(str);
if(pos==str.length()) System.out.println("未找到");
else
System.out.println("位置为"+pos);
System.out.println("真实位置为"+str.indexOf("needle"));
}
}