字符串操作相关算法
子串在主串中的定位操作:串的模式匹配
1. 一般性的匹配方法
(1)相当于 string类的find方法-----找子串在主串中第一次出现的位置
1 int findSub(string str,string sub) 2 { 3 int strLen = str.size(); 4 int subLen = sub.size(); 5 int i = 0,j = 0; 6 while(i<strLen && j<subLen) { 7 if(str[i] == sub[j]) { 8 ++i; 9 ++j; 10 } 11 else { 12 i = i - j + 1; //这个很关键,i只前进一步 13 j = 0; 14 } 15 } 16 if (j == subLen) { 17 //匹配成功 18 return (i-j); 19 } 20 else { 21 return -1; 22 } 23 }
(2)换成for循环
int findSub_v2(string str,string sub) { int strLen = str.size(); int subLen = sub.size(); for(int i=0;i<(strLen-subLen+1);i++) { int j = 0; for(j=0;j<subLen;j++) { if(str[i+j] == sub[j]) { continue; } else { break; } } if(j == subLen) { return i; } } return -1; }
代码同(1)的没有什么区别
设主串长n,子串长m
最好情况:子串在最前面,只要第一次循环一次,第二个循环m次
最差情况:子串在最后面,一共要循环 (n-m+1)*m 次 O(mn)的时间复杂度
2. KMP 模式匹配算法 O(m+n)
note:(1)、(2)中的图从《大话》中截来,它里面的字符下标从1开始
(1)下面是一般的匹配算法:
while形式的循环里面,每次遇到不等的字符的时候,i要回溯(回溯的位置i-j+1使得i每次循环完子串后只增长1个单位,也就相当于版本2的for循环)。
原来的实现基本意思是:每次比较到不等地方,子串下标从0开始,而主串只加1
问题:
上面第②步,因为子串中第一个和第二个字符不等,而子串的第二个字符与主串的第二个字符相等,所以没必要进行第②步
上面的那么多步,实际上只需要下面两步:
(2)子串中有重复字符的情况
实际上,只需要第①、⑥步:
(3)KMP
在while循环里,使i不回溯(每比较一次,i就自增一次)。根据子串本身的特点,每次调整 j 的位置
j 的值:next数组
如果没有重复字符,则子串下标每次要回到 0,而主串下标依然在之前比较却不相等的那个地方。
如果有重复字符:YY一下(2)中的内容,如果我们比较到第3个字符'c'不等的话,那还是要从子串的下标0开始;
如果比较到4个字符'a'不等,那还是要回到0
如果比较到第5个字符'b'不等,则只要回到1就行了(这里实际上还可以优化,即KMP的优化,在(4))
如果比较到第6个字符'x'不等,则j回到第3个字符进行比较就可以了
代码如下:参见 https://segmentfault.com/a/1190000008575379
未优化的:
static void getNext(string p,int next[]) { int len = p.size(); int i = 0; int j = -1; next[0] = -1; while(i<len) { if(j==-1 || p[i] == p[j]) { i++; j++; next[i] = j; } else { j = next[j]; } } }
优化后的求next数组:
static void getNext_v2(string p,int next[]) { int len = p.size(); int i = 0; int j = -1; next[0] = -1; while(i<len) { if(j==-1 || p[i] == p[j]) { i++; j++; if(p[i] != p[j]) { next[i] = j; } else { next[i] = next[j]; } } else { j = next[j]; } } }
kmp搜索算法本身:用while循环
1 int kmpSearch(string str,string sub) 2 { 3 int strlen = str.size(); 4 int sublen = sub.size(); 5 int * next = (int *)malloc((sublen+1)*sizeof(int)); 6 if(next == nullptr) { 7 return -1; 8 } 9 int i = 0; 10 int j = 0; 11 // getNext(sub,next); 12 getNext_v2(sub,next); 13 while(i<strlen && j<sublen) { 14 if(j==-1 || str[i] == sub[j]) { 15 i++; 16 j++; 17 } 18 else { 19 j = next[j]; //只是这里与(1)里面的不同,i不再回溯了 20 } 21 } 22 if(j == sublen) { 23 //匹配上了 24 return i-j; 25 } 26 if(next != nullptr) { 27 free(next); 28 } 29 return -1; 30 }