1.简单字符串匹配
(1)问题描述
如给定字符串str1 = "abcaad" ,str2="",判断str1中是否包含str2
(2)思路
使用2个指针i、j,如果i,j指针指向字符相同,那么i++、j++;否则,j=0,i=i-j+1。
(3)代码
String str1 = "cdbabcdabdsd";
String str2 = "abcdabx";
int i = 0;
int j = 0;
while(i<str1.length() && j<str2.length())
{
if(str1.charAt(i) == str2.charAt(j))
{
i++;
j++;
}
else
{
i = i-j+1;
j = 0;
}
}
if(j==str2.length())
System.out.println("find");
else
System.out.println("not find");
2.kmp算法
(1)思想
由于简单匹配算法在匹配失败的时候,j=0,i每次都要回溯,从而造成时间复杂度过高。那么就需要使用一个数组,用来保存失配的时候,j指针回溯到何处,而i指针不回溯。那么算法就可以如下写:
String str1 = "cdbabcdabdsd";
String str2 = "abcdabx";
int next[] = new int[str2.length()];
getNext(str2,next); //设置next数组的值
int i = 0;
int j = 0;
while(i<str1.length() && j<str2.length())
{
if(j==-1 || str1.charAt(i)==str2.charAt(j) )
{
i++;
j++;
}
else
j = next[j]; //失配的时候i不变,j从next中找到需要回溯的位置
}
if(j==str2.length())
{
System.out.println(str1.substring(i-j, i));
}
那么现在问题就是如何设置next数组的值?首先需要明确以下几点:
1)next[i]表示的是下标为i的字符失配了,应该回溯到什么位置。开始时设置next[0] = -1,表示第一个字符就没匹配
2)next数组实际上是字符串和自身的匹配,和其他串完全没有关系
现在讨论一般情况:
1>i == 0时,next[i] = -1。原因上面已经讲了
2>i != 0时:此时假设next[i] = k,即如果在str2第i个字符失配了,那么就回溯到第k个字符。
如果str2[i] == str2[k],那么next[++i] == ++k。
如果str2[i] != str2[k],那么 k = next[i]。
此处可以这么理解:虽然是str2自身的匹配,但是可以将str2[0-i]视为一个字符串s1 ,str2[0-k]视为另一个字符串s2,,那么由next[i] = k的就可以得出
str2[i-1]==str2[k-1],因为根据next定义可得在i失配可以回溯到k,那么如果str2[i] == str2[k], 就可以说在i+1失配的时候可以回溯到k+1(或者更简单的理解就是两个字符串第i和第k个字符相等,那么肯定就要比较下一个了)
如果str2[i] != str2[k],那么还是kmp,第二个字符串的k应该变为next[k]
(2)代码
private void getNext(String str, int[] next)
{
next[0] = -1;
int j = 0;
int k = -1;
while(j<(str.length()-1)) //因为next[str.length()-1]在前一轮的计算中会得出,所以只用到str.length()-1即可
{
if(k==-1 ||str.charAt(j)==str.charAt(k) )
{
j++;
k++;
next[j] = k;
}
else
k = next[k];
}
}