串首开始的最长回文串长
构造 S = s + "#" + reverse(s),原题就是求S中的前缀等于后缀的最大长度。正是KMP算法预处理pattern求各前缀的最大边界,只要最后返回b[S.size()]。
[kmp算法](http://www.inf.fh-flensburg.de/lang/algorithmen/pattern/kmpen.htm
把前缀等于后缀的部分叫做边界,0<=len(border)<N,求最大边界。
- 性质1:如果s是x的边界、r是s的边界,r也是x的边界,即边界的边界也是边界。
|-r-| |-r-| |-r-| |-r-|
|----s----|--------|----s----|
|--------------x-------------|
- 性质2:r是x的边界,后缀边界和前缀边界后的字符相同,则边界扩展1。
|--r--|a |--r--|a
|-------------x--------------|a
设b[i]表示前缀x[0..i-1]的最大边界长。初始设b[0]=-1,空串无边界。假设已解决子问题b[i]、b[i-1]等,现在考虑b[i+1]。
设j=b[i],当x[i]==x[j],由性质2,b[i+1]=j+1;否则,由性质1,继续尝试下一边界j=b[j],直到j=-1,这时刚好b[i+1]=j+1。
写法规整后就是维护不变式:i是前缀长、j是边界长
vector<int> buildBorder(const string& pattern) {
const int M = pattern.size();
// 设b[i]表示长为i的前缀pattern[0..i-1]的最大边界长
vector<int> b(M + 1);
int i = 0, j = -1; // 空串,无边界
b[i] = j;
while (i < M) {
while (j >= 0 && pattern[i] != pattern[j]) { // 当前边界不能扩展,性质2
j = b[j]; // 考虑下一小的边界,性质1
}
// 扩展边界
i++, j++;
b[i] = j;
}
return b;
}
int kmp(string pattern, string text) {
const int N = text.size(), M = pattern.size();
vector<int> b = buildBorder(pattern);
int ans = 0;
int t = 0, p = 0;
while (t < N) {
while (p >= 0 && text[t] != pattern[p]) {
p = b[p];
}
t++, p++;
if (p == M) {
ans++; // 匹配成功
p = b[p]; // 继续寻找下一匹配
}
}
return ans;
}