标签:
Manacher(马拉车)是一种求最长回文串的线性算法,复杂度O(n)。网上对其介绍的资料已经挺多了的,请善用搜索引擎。
而扩展KMP说白了就是是求模式串和主串的每一个后缀的最长公共前缀【KMP更像是一个自动机】
题目:
求原字符串最少增加几个字符后可变成回文串,相当于求最长回文子序列的长度。
解法:直接求串S和反转串Sr的最长公共子序列。
#include <cstdlib>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define ll long long
#define maxn 10009
#define MAX 1<<30
#define Q 1000000007
using namespace std;
int n, dp[2][5001];
char s[5001];
int main()
{
scanf("%d%s", &n, s);
rep(i, 1, n) rep(j, 1, n)
{
dp[i%2][j] = max(dp[(i-1)%2][j], dp[i%2][j-1]);
if (s[i-1]==s[n-j] && dp[(i-1)%2][j-1]+1>dp[i%2][j]) dp[i%2][j] = dp[(i-1)%2][j-1]+1;
}
printf("%d\n", n-dp[n%2][n]);
return 0;
}
模板题。
#include <cstdlib>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define ll long long
#define maxn 234567
#define MAX 1<<30
#define Q 1000000007
using namespace std;
int n, l, k[maxn], o, p, ans;
char c[maxn], s[maxn];
int main()
{
while (scanf("%s", c) == 1)
{
l = strlen(c);
rep(i, 0, l-1) s[i*2+1] = ‘$‘, s[i*2+2] = c[i];
l = l*2+1; s[l]=s[l+1] = ‘$‘; s[0] = ‘#‘;
o=p=ans=0;
rep(i, 0, l)
{
k[i] = i<p ? min(k[2*o-i], p-i) : 1;
while (s[i+k[i]] == s[i-k[i]]) k[i]++;
if (i+k[i]>p) p = i+k[i], o = i;
ans = max(ans, k[i]-1);
}
printf("%d\n", ans);
}
return 0;
}
同模板题。
#include <cstdlib>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define ll long long
#define maxn 2000009
#define MAX 1<<30
#define Q 1000000007
using namespace std;
int t, n, l, k[maxn], o, p, ans;
char c[maxn], s[maxn];
int main()
{
while (++t)
{
scanf("%s", c);
if (c[0]==‘E‘) break; l = strlen(c);
rep(i, 0, l-1) s[i*2+1]=‘$‘, s[i*2+2]=c[i];
l=l*2+1; s[0]=‘#‘; s[l]=s[l+1]=‘$‘;
ans=o=p=0;
rep(i, 1, l)
{
k[i] = i<p ? min(p-i, k[o*2-i]) : 1;
while (s[i+k[i]] == s[i-k[i]]) k[i]++;
if (i+k[i]>p) p=i+k[i], o=i;
ans = max(ans, k[i]-1);
}
printf("Case %d: %d\n", t, ans);
}
return 0;
}
求最长双倍回文。
我实在是太弱了,于是写了个暴力+最优性剪枝,成功地用O(n^2)的算法AC!【其实正解是要O(nlogn)的算法的】
#include <cstdlib>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define ll long long
#define maxn 1000009
#define MAX 1<<30
#define Q 1000000007
using namespace std;
int t, n, l, k[maxn], o, p, ans, b[maxn];
char c[maxn], s[maxn];
int main()
{
scanf("%d%s", &l, c);
rep(i, 0, l-1) s[i*2+1]=‘$‘, s[i*2+2]=c[i];
s[0]=‘#‘; s[l*2+1]=‘$‘;
ans=o=p=0;
rep(i, 1, l*2+1)
{
k[i] = i<p ? min(p-i, k[o*2-i]) : 1;
while (s[i+k[i]] == s[i-k[i]]) k[i]++;
if (i+k[i]>p) p=i+k[i], o=i;
}
rep(i, 1, l) b[i+1]=k[i*2-1]/2;
rep(i, 1, l)
down(j, b[i]/2, ans+1) if (b[i-j]>=j && b[i+j]>=j) ans = j;
printf("%d\n", ans*4);
return 0;
}
求最长双回文串。
对于每个字符,利用单调性质求出往左往右所能找到的最长的回文串。
#include <cstdlib>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define ll long long
#define maxn 200009
#define MAX 1<<30
#define Q 1000000007
using namespace std;
int t, n, l, k[maxn], o, p, ans, lt[maxn], rt[maxn];
char c[maxn], s[maxn];
int main()
{
scanf("%s", c); l=strlen(c);
rep(i, 0, l-1) s[i*2+1]=‘$‘, s[i*2+2]=c[i];
s[0]=‘#‘; s[2*l+1]=‘$‘;
o = p = 0;
rep(i, 1, l*2+1)
{
k[i] = i<p ? min(p-i, k[2*o-i]) : 1;
while (s[i+k[i]] == s[i-k[i]]) k[i]++;
if (i+k[i]>p) p = i+k[i], o = i;
}
o = 1; rep(i, 2, l*2+1)
{
while (o+k[o]<=i) o++;
lt[i] = i-o;
}
o = l*2+1; down(i, l*2, 1)
{
while (i<=o-k[o]) o--;
rt[i] = o-i;
}
rep(i, 1, l*2+1) if (s[i]==‘$‘ && lt[i] && rt[i] && ans < lt[i]+rt[i]) ans = lt[i]+rt[i];
printf("%d\n", ans);
return 0;
}
这题貌似EXKMP和Manacher都可以做。
#include <cstdlib>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define ll long long
#define maxn 500009
#define MAX 1<<30
#define Q 1000000007
using namespace std;
int tt, l, n[maxn], ex[maxn], v[maxn], t[26];
char c[maxn], s[maxn];
int main()
{
scanf("%d", &tt);
while (tt--)
{
rep(i, 0, 25) scanf("%d", &t[i]);
scanf("%s", s); int l = strlen(s);
rep(i, 0, l-1) c[i] = s[l-i-1];
int k, p, a; rep(i, 1, l-1) v[i] = 0;
rep(i, 0, l-1) ex[i] = n[i] = 0;
n[1] = 0; while (c[n[1]] == c[n[1]+1]) n[1]++;
n[0] = l; k = 1; p = n[1];
rep(i, 2, l-1)
{
n[i] = i<p ? min(n[i-k], l-i) : 0;
while (n[i]+i<l && c[n[i]] == c[n[i]+i]) n[i]++;
if (i+n[i]>p) p = i+n[i], k = i;
}
k = p = 0;
rep(i, 0, l-1)
{
ex[i] = i<p ? min(n[i-k], p-i) : 0;
while (ex[i]+i<l && c[ex[i]] == s[ex[i]+i]) ex[i]++;
if (i+ex[i]>p) p = i+ex[i], k = i;
}
a = 0; rep(i, 1, l-1) { a+=t[s[l-i]-‘a‘]; if (ex[l-i]==i) v[i]+=a; }
rep(i, 0, l-1) ex[i] = n[i] = 0;
n[1] = 0; while (s[n[1]] == s[n[1]+1]) n[1]++;
n[0] = l; k = 1; p = n[1];
rep(i, 2, l-1)
{
n[i] = i<p ? min(n[i-k], p-i) : 0;
while (n[i]+i<l && s[n[i]] == s[n[i]+i]) n[i]++;
if (i+n[i]>p) p = i+n[i], k = i;
}
k = p = 0;
rep(i, 0, l-1)
{
ex[i] = i<p ? min(n[i-k], p-i) : 0;
while (ex[i]+i<l && s[ex[i]] == c[ex[i]+i]) ex[i]++;
if (i+ex[i]>p) p = i+ex[i], k = i;
}
a = 0; rep(i, 1, l-1) { a+=t[c[l-i]-‘a‘]; if(ex[l-i]==i) v[l-i]+=a; }
int ans = 0; rep(i, 1, l-1) ans = max(ans, v[i]); printf("%d\n", ans);
}
return 0;
}
扩展KMP的实例。。。
【代码莫名写WA了】
#include <cstdlib>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define ll long long
#define maxn 300009
#define MAX 1<<30
#define Q 1000000007
using namespace std;
int tt, n[maxn];
char s[maxn];
int main()
{
scanf("%d", &tt); int t = 0;
while (t++<tt)
{
scanf("%s", s); int l = strlen(s);
rep(i, 0, l-1) s[l+i] = s[i];
int k, p;
n[0] = l*2; n[1] = 0; while (s[n[1]] == s[n[1]+1]) n[1]++; p = n[k=1]+1;
rep(i, 2, l-1)
{
n[i] = i<p ? min(p-i, n[i-k]) : 0;
while (s[n[i]] == s[n[i]+i]) n[i]++;
if (i+n[i]>p) p = i+n[i], k = i;
}
int q=0, w=0, e=0;
rep(i, 0, l-1) if (n[i]>=l) w++; else if (s[n[i]] < s[i+n[i]]) e++; else q++;
printf("Case %d: %d %d %d\n", t, q, w, e);
}
return 0;
}
第三道模板题。
#include <cstdlib>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define ll long long
#define maxn 2009
#define MAX 1<<30
#define Q 1000000007
using namespace std;
int t, n, l, k[maxn], o, p, ans, b[maxn];
char c[maxn], s[maxn];
int main()
{
scanf("%s", c); l=strlen(c);
rep(i, 0, l-1) s[i*2+1]=‘$‘, s[i*2+2]=c[i];
s[0]=‘#‘; s[2*l+1]=‘$‘;
ans = o = p = 0;
rep(i, 1, l*2+1)
{
k[i] = i<p ? min(p-i, k[2*o-i]) : 1;
while (s[i+k[i]] == s[i-k[i]]) k[i]++;
if (i+k[i]>p) p = i+k[i], o = i;
if (k[ans]<k[i]) ans = i;
}
for(int i = ans-k[ans]+2; i <= ans+k[ans]-1; i+=2) printf("%c", s[i]);
return 0;
}
总之,Manacher、KMP和ExKMP所利用的就是之前已经计算过的信息,以减少无意义的匹配。
标签:
原文地址:http://www.cnblogs.com/NanoApe/p/4297236.html