KMP && exkmp && manacher
1.KMP
kmp是用来字符串匹配的一种算法,核心是next数组的使用,如果暴力去匹配,假设a[n], b[m], 那么复杂度为o(nm)的,但其实可以通过next数组(前缀与后缀的最大匹配值)来降低到o(n +m), 意思就是比如a[i + 1] != b[j + 1], 那么暴力来说,就只在从0重新开始匹配,但是其实不是每一次都返回到0的,只要返回到前缀的后一个等于b[j+1], 因为b[n ~ j] 和b[0 ~ n -1]是一样的,若b[j + 1]不匹配则,返回到b[n]看看b[n + 1]是否匹配。也就是next数组的含义。
for (int i = 2, j = 0; i <= n; i ++) { while(j && a[i] != a[j + 1]) j = ne[j]; if(a[i] == a[j + 1]) j ++; ne[i] = j; } for (int i = 1, j = 0; i <= m; i ++) { while(j && b[i] != a[j + 1]) j = ne[j]; if(b[i] == a[j + 1]) j++; if(j == n) { } }
Kmp有一个循环节,就是周期通过len - next[len]求得。
2.EXKMP
exkmp是kmp的拓展,用来判断s1[k...n]与s2[0...m]的前缀最大匹配度,使用ne数组和ex数组,分别保存的是s2[k...]与s2[0...m]的前缀最大匹配度,ex是s1[k...n]与s2[0....m]的前缀最大匹配度,再用po和j代表匹配最大的起点和终点,第一种其中匹配的ne[i-po] + i小于ne[po]+po说明匹配的还在最大段里,那么ne[i-po]等于ne[i],若大于等于了则要手动匹配过去j = ne[po] + po - i与i + j开始匹配,然后更新po。然后同样的方法得到ex数组。
void getne(char *str) { int i = 0, j, po, len = strlen(str); ne[0] = len; while(str[i] == str[i + 1] && i + 1 < len) i++; ne[1] = i; po = 1; for (int i = 2; i < len; i ++) { if(ne[i - po] + i < ne[po] + po) ne[i] = ne[i - po]; else { j = ne[po] + po - i; if(j == -1) j = 0; while(i + j < len && str[j] == str[i + j]) j ++; ne[i] = j; po = i; } } } void exkmp(char *s1, char *s2) { int i = 0, j, po, len = strlen(s1), l2 = strlen(s2); getne(s2); while(s1[i] == s2[i] && i < len && i < l2) i ++; ex[0] = i; po = 0; for (int i = 1; i < len; i ++) { if(ne[i - po] + i < ex[po] + po) { ex[i] = ne[i - po]; } else { j = ex[po] + po - i; if(j == -1) j = 0; while(i + j < len && j < l2 && s1[j + i] == s2[j]) j ++; ex[i] = j; po = i; } } }
3.MANACHER
用来判断一个字符中的最大回文串,利用回文串的性质,降低复杂度。用p[i]表示以s[i]为中心的最大回文串半径,p[i] - 1就是原来的原串匹配长度。那么问题就变成了求p[i],首先字符有奇偶,偶数不好操作,在每个字符中间加入‘#’,s[0]加入‘@’(防止越界)。
同意用id来表示最大回文串的中心,p[id] + id就是要匹配的字符。那么产生的那种情况,一种是mx大于i 那么根据回文串就会有一个对称的点 2 * id - i 比较p[2 * id ] - i 与mx - i的大小,然后手动匹配,若mx小于等于i,那么就要手动更新i + p[i] 和i - p[i]。再更新id和mx。
int len = strlen(str); for (int i = len; i >= 0; i --) { str[(i << 1) + 1] = '#'; str[(i << 1) + 2] = str[i]; } str[0] = '@'; len = len * 2 + 2; manacher(str, len); void manacher(char *s, int len) { p[0] = 1; int mx = 0, id = 0; for (int i = 1; i < len; i ++) { p[i] = mx > i ? min(mx - i, p[id * 2 - i]) : 1; while(s[i + p[i]] == s[i - p[i]]) p[i] ++; if(i + p[i] > id + p[id]) { id = i; mx = i + p[i]; } } }