题目大意:
就是现在有一个长度为奇数的个回文串串T被加密成了 A + prefix + B + middle + C + suffix的形式, 其中 T = prefix + middle + suffix , 这六个部分可以是空的, 现在对于给出的加密后的串求原串T的最大可能长度, 其中middle长度也是奇数, preifx长度和suffix长度相等, 输入的字符串长度<= 10^5
大致思路:
首先很容易观察到middle的中心就是T的中心, middle也是回文串, 先Manacher处理出每个字符为中心的回文半径, 那么枚举T的正中心将[i - R[i] + 1, i + R[i] - 1]作为middle一定是以i为中心下的最优解, 然后考虑最大化suffix和prefix, 用KMP预处理出以 j 位置字符为结尾的能和suffix匹配最大长度的字符串的长度, 即 match[j] 表示 T[j - k + 1, ... j - 1, j] == T'[0...k - 1] 的最大的k, T‘表示T的反向字符串, 那么预处理前缀和dp[i] = max(dp[i - 1], match[i])来表示区间[1, i]结尾的字符串中能匹配的最大长度, 于是剩下的就很明了了= =
这个思想是参见2014年国家集训队徐毅的论文上的, 可以去参考一下那篇论文
代码如下:
Result : Accepted Memory : 2164 KB Time : 62 ms
/* * Author: Gatevin * Created Time: 2015/3/29 9:26:17 * File Name: Chitoge_Kirisaki.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; #define maxn 100010 char s[maxn]; int R[maxn]; char rev[maxn]; int next[maxn]; int match[maxn]; int dp[maxn]; int tail[maxn]; void Manacher(char *s, int *R, int n) { int mx = 0, p = 0; R[0] = 1; for(int i = 1; i <= n; i++) { if(mx > i) R[i] = min(R[2*p - i], mx - i); else R[i] = 1; while(s[i - R[i]] == s[i + R[i]]) R[i]++; if(i + R[i] > mx) mx = i + R[i], p = i; } return; } void KMP(char *s, char *rev, int n) { memset(next, 0, sizeof(next)); for(int i = 1; i < n; i++) { int j = i; while(j > 0) { j = next[j]; if(rev[i] == rev[j]) { next[i + 1] = j + 1; break; } } } memset(match, 0, sizeof(match)); for(int i = 1, j = 0; i < n + 1; i++) if(s[i] == rev[j]) j++, match[i] = j; else while(j > 0) { j = next[j]; if(s[i] == rev[j]) { j++; match[i] = j; break; } } return; } vector <pair<int, int> > pos; int main() { scanf("%s", s + 1); int n = strlen(s + 1); s[0] = '@', s[n + 1] = '$'; Manacher(s, R, n + 1); for(int i = 0; i < n; i++) rev[i] = s[n - i]; rev[n] = '\0'; KMP(s, rev, n); int maxl = 0; dp[1] = match[1]; tail[1] = 1; for(int i = 1; i <= n; i++) { if(dp[i - 1] < match[i]) dp[i] = match[i], tail[i] = i; else dp[i] = dp[i - 1], tail[i] = tail[i - 1];//tail[i]记录dp[i]对应最优解的串的末尾 } for(int i = 1; i <= n; i++)//枚举middle中心 { int middle = 2*R[i] - 1; int pre_suf = min(dp[i - R[i]], n - (i + R[i] - 1));//prefix或suffix长度 if(middle + 2*pre_suf > maxl) { pos.clear(); maxl = middle + 2*pre_suf; if(pre_suf > 0) pos.push_back(make_pair(tail[i - R[i]] - pre_suf + 1, pre_suf)); pos.push_back(make_pair(i - R[i] + 1, 2*R[i] - 1)); if(pre_suf > 0) pos.push_back(make_pair(n - pre_suf + 1, pre_suf)); } } printf("%d\n", (int)pos.size()); for(unsigned int i = 0; i < pos.size(); i++) printf("%d %d\n", pos[i].first, pos[i].second); return 0; }