字符串算法有哪些呢???Tire,BM,KMP,AC自动机,后缀数组,后缀自动机,RK,Shift-And/Or,Manacher.....
™这么这么多啊!!!
也只能慢慢学了。。。【后期:好多算法都是没用的鬼。。
接下来的题是按我做题顺序来排的,难度的话我就不理了(`・ω・´)
先是BZOJ。。。
BZOJ 2434: [NOI2011]阿狸的打字机
第一道字符串就做这道简直大丈夫!
先建棵ACTree和FailTree,保存每个字符串的末节点位置。
然后从树根一步一步走,根至当前节点的字符串就是当前打字机内的字符串,一边走一边离线查询。
对于每个查询(x,y)在查到s[y]的末尾节点的时候开始计算,用DFS序思想和树状数组求s[x]的末尾节点的Fail子树中包含了几个s[y]的节点,这就是答案了。
当时AC自动机不会,FailTree不会,后缀数组不会,DFS序没想到。。。QAQ
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>
#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 MS 123456
#define MAX 1037471823
using namespace std;
struct node
{
int x, y, n;
bool operator < (const node &k) const { return x < k.x; };
} e[MS]; int ec, fir[MS];
int c[MS], r[MS][26], f[MS], h[MS], tc, ss[MS], ds[MS], dt[MS], k[MS];
queue<int> p;
int n, m, x, y, now, nq;
char s[MS];
void DFS(int x)
{
int o = fir[x]; ds[x] = ++now;
while (o) { DFS(e[o].y); o = e[o].n; }
dt[x] = now;
}
inline void Add(int x, int z)
{ int o = x; while (o<=tc+1) k[o]+=z, o = o+(o&(-o)); }
inline int Qsum(int x)
{
int o = x, ans=0; while (o) ans+=k[o], o = o-(o&(-o));
return ans;
}
inline void Query(int x)
{
while (e[nq].x == x)
{ int y=e[nq].y; e[nq].y = Qsum(dt[ss[y]]) - Qsum(ds[ss[y]]-1); nq++; }
}
int main()
{
scanf("%s", s); int l=strlen(s); f[0] = -1;
rep(i, 0, l-1)
if (s[i]!='P' && s[i]!='B')
{ if (!r[now][s[i]-'a']) tc++, r[now][s[i]-'a']=tc, h[tc]=now, c[tc]=s[i]-'a'; now=r[now][s[i]-'a']; }
else if (s[i]=='P') ss[++n] = now;
else now = h[now];
rep(i, 0, tc) f[i] = -1; p.push(0);
while (!p.empty())
{
x = p.front(); p.pop();
rep(i, 0, 25) if (r[x][i])
{
int fo = f[x];
while (fo >= 0)
{ if (r[fo][i]) break; else fo = f[fo]; }
if (fo < 0) f[r[x][i]] = 0; else f[r[x][i]] = r[fo][i];
p.push(r[x][i]);
}
}
down(i, tc, 1) e[i].y = i, e[i].n = fir[f[i]], fir[f[i]] = i; now = 0; DFS(0);
rep(i, 1, tc) fir[i] = 0;
scanf("%d", &m);
rep(i, 1, m)
{ scanf("%d%d", &x, &y); e[i].x = y, e[i].y = x, e[i].n = i; }
sort(e+1, e+1+m);
now=n=0; nq=1; rep(i, 0, l-1)
if (s[i]!='P' && s[i]!='B') { now=r[now][s[i]-'a']; Add(ds[now], 1); }
else if (s[i]=='P') Query(++n); else { Add(ds[now], -1); now = h[now]; }
rep(i, 1, m) e[i].x = e[i].n; sort(e+1, e+1+m); rep(i, 1, m) printf("%d
", e[i].y);
return 0;
}
BZOJ 3172: [TJOI2013]单词
依旧FailTree搞,建好之后就遍历+统计,然后s[i]的出现次数就是末尾节点的Fail子树所包含的其他串的节点个数(重复出现也要算)。
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>
#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 MS 1234567
#define MAX 1037471823
using namespace std;
struct node { int y, z; } e[MS]; int fir[MS];
int n, c[MS], k[MS], r[MS][26], f[MS], tc, ss[MS], x;
queue<int> q;
char s[MS];
void Add(int n)
{
int o = 0, l = strlen(s);
rep(i, 0, l-1)
{
if (!r[o][s[i]-'a']) tc++, r[o][s[i]-'a'] = tc, c[tc] = s[i]-'a';
o = r[o][s[i]-'a'], k[o]++;
}
ss[n] = o;
}
void DFS(int x)
{
int o = fir[x];
while (o)
{ DFS(e[o].y); k[x] += k[e[o].y]; o = e[o].z; }
}
int main()
{
scanf("%d", &n);
rep(i, 1, n) { scanf("%s", s); Add(i); }
q.push(0); f[0] = -1;
while (!q.empty())
{
x = q.front(); q.pop();
rep(i, 0, 25) if (r[x][i])
{
int fo = f[x];
while (fo >= 0 && !r[fo][i]) fo = f[fo];
if (fo < 0) f[r[x][i]] = 0; else f[r[x][i]] = r[fo][i]; q.push(r[x][i]);
}
}
rep(i, 1, tc) e[i].y = i, e[i].z = fir[f[i]], fir[f[i]] = i; DFS(0);
rep(i, 1, n) printf("%d
", k[ss[i]]);
return 0;
}
BZOJ 1030: [JSOI2007]文本生成器
数位DP+AC自动机,没了。
#include <queue>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#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 MS 6789
#define MAX 1037471823
#define Q 10007
using namespace std;
struct node{ int y, n; }e[MS]; int fir[MS];
int n, m, c[MS], r[MS][26], f[MS], tc, dp[123][MS], ans, x, y;
bool b[MS]; char s[MS];
queue <int> q;
void Add()
{
int l = strlen(s), o = 0;
rep(i, 0, l-1)
{ if (!r[o][s[i]-'A']) tc++, r[o][s[i]-'A'] = tc, c[tc] = s[i]-'A'; o = r[o][s[i]-'A']; }
b[o] = true;
}
void DFS(int x)
{
int o = fir[x]; b[x] = true;
while (o) { DFS(e[o].y); o = e[o].n; }
}
/* void Query(int x, int n)
{
if (x == 1) rep(i, 0, tc) rep(j, 0, tc) dp[n][i][j] = k[i][j] % Q;
else if (x % 2==1)
{
Query(x-1, n+1);
rep(i, 0, tc) rep(j, 0, tc) rep(o, 0, tc) dp[n][i][j] += dp[n+1][i][o] * k[o][j], dp[n][i][j]%=Q;
}
else
{
Query(x/2, n+1);
rep(i, 0, tc) rep(j, 0, tc) rep(o, 0, tc) dp[n][i][j] += dp[n+1][i][o] * dp[n+1][o][j], dp[n][i][j]%=Q;
}
} */
int Mult(int x)
{
if (x == 1) return 26; else if (x % 2 == 1) return (Mult(x-1)*26)%Q; else { int a = Mult(x/2); return (a*a)%Q; }
}
int main()
{
scanf("%d%d", &n, &m);
rep(i, 1, n) { scanf("%s", s); Add(); }
q.push(0); f[0] = -1;
while (!q.empty())
{
x = q.front(); q.pop();
rep(i, 0, 25) if (r[x][i])
{
y = f[x];
while (y >= 0 && !r[y][i]) y = f[y];
if (y < 0) f[r[x][i]] = 0; else f[r[x][i]] = r[y][i]; q.push(r[x][i]);
}
}
rep(i, 1, tc) e[i].y = i, e[i].n = fir[f[i]], fir[f[i]] = i;
rep(i, 1, tc) if (b[i]) DFS(i);
dp[0][0] = 1;
rep(k, 0, m-1)
rep(i, 0, tc) if (!b[i] && dp[k][i])
{
rep(j, 0, 25)
{
y = i;
while (y >= 0 && !r[y][j]) y = f[y];
if (y < 0) dp[k+1][0]+=dp[k][i], dp[k+1][0]%=Q; else dp[k+1][r[y][j]]+=dp[k][i], dp[k+1][r[y][j]]%=Q;
}
}
rep(i, 0, tc) if (!b[i]) ans = (ans + dp[m][i])% Q;
/* Query(m, 1);
rep(i, 0, tc) if (!b[i]) ans = (ans + dp[1][0][i])%Q; */
ans = Mult(m) - ans; while (ans < 0) ans += Q;
printf("%d
", ans);
return 0;
}
BZOJ 2754: [SCOI2012]喵星球上的点名
建AC自动机,然后对于每次询问就查找询问串的Fail子树所包含那些字符串的节点,然后被点到名字的就次数+1。
结果AC是AC了,时间跑出了个11s。。。
吐血。。。
那么我们换成后缀数组来做:
把所有出现过的串全部连成一串(中间插字符),然后后缀一遍,接着从上到下找,对于以询问串作为前缀的后缀s[i],我们就找出所有和s[i]的公共前缀长度大于询问串长度的后缀,然后统计去重。。。
嗯AC,2s
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cmath>
#include <set>
#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 MS 345678
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, s;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, m, x, y, l, s[MS], k[MS], sa[MS], r[MS], ss[MS], c1[MS], c2[MS], h[MS], b[MS];
int main()
{
scanf("%d%d", &n, &m);
rep(i, 1, n*2)
{ scanf("%d", &x); rep(j, 1, x) { scanf("%d", &s[++l]); k[l] = (i+1)/2; } s[++l] = -1; }
rep(i, 1, m)
{ k[l+1] = -i; scanf("%d", &ss[i]); rep(j, 1, ss[i]) scanf("%d", &s[++l]); s[++l] = -1; }
x = 1; rep(i, 1, l) r[i] = s[i];
while (x <= l)
{
rep(i, 1, l) q[i].x = r[i], q[i].y = i+x>l?0:r[i+x], q[i].s = i;
sort(q+1, q+1+l);
y = r[q[1].s] = 1;
rep(i, 2, l) if ((q[i].x != q[i-1].x) || (q[i].y != q[i-1].y)) r[q[i].s] = ++y; else r[q[i].s] = y;
if (y == l) break; else x *= 2;
}
rep(i, 1, l) sa[r[i]] = i;
x = 0; rep(i, 1, l)
{
if (x) x--;
int j = sa[r[i]-1];
while (s[i+x]==s[j+x]) x++;
h[r[i]] = x;
}
rep(i, 1, l) if (k[i] < 0)
{
y = -k[i];
x = r[i];
while (h[x] >= ss[y])
{
if (k[sa[x-1]] > 0 && b[k[sa[x-1]]] != y)
b[k[sa[x-1]]] = y, c1[k[sa[x-1]]]++, c2[y]++;
x--;
}
x = r[i]+1;
while (h[x] >= ss[y])
{
if (k[sa[x]] > 0 && b[k[sa[x]]] != y)
b[k[sa[x]]] = y, c1[k[sa[x]]]++, c2[y]++;
x++;
}
}
rep(i, 1, m) printf("%d
", c2[i]);
rep(i, 1, n) printf("%d%c", c1[i], i==n?'
':' ');
return 0;
}
然后Ecstasy大神扔了份后缀数组论文的pdf给我。。。
POJ 1743: Musical Theme
求不重叠·最长·重复·子串的长度。
二分答案+分组统计(后缀数组最重要最常用的思想),判断组内Sa的极差。
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#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 MS 23456
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, s[MS], h[MS], sa[MS], rk[MS], a, p, l, r;
bool Check(int k)
{
int bs = 0, ss = 0;
rep(i, 1, n)
if (h[i] < k) if (bs - ss >= k) return true; else bs = ss = sa[i]; else
{ if (ss > sa[i]) ss = sa[i]; if (bs < sa[i]) bs = sa[i]; }
if (bs - ss >= k) return true; else return false;
}
int main()
{
scanf("%d", &n);
while (n)
{
rep(i, 1, n) scanf("%d", &s[i]);
rep(i, 1, n-1) s[i] = s[i+1] - s[i]; n--;
rep(i, 1, n) rk[i] = s[i];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i;
sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a;
else rk[q[i].z] = a;
if (a == n) break; else p*=2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[i+a] == s[p+a]) a++; h[rk[i]] = a;
}
l = 0, r = n/2; while (l != r) if (Check((l+r)/2+1)) l = (l+r)/2+1; else r = (l+r)/2;
if (l < 4) printf("0
"); else printf("%d
", l+1); scanf("%d", &n);
}
return 0;
}
POJ 3261: Milk Patterns
求可重叠·最长·至少重复K次·子串的长度。
依旧二分答案+分组统计,判断组内后缀的个数。
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#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 MS 23456
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, s[MS], h[MS], sa[MS], rk[MS], a, p, l, r, c;
bool Check(int k)
{
int a = 0;
rep(i, 1, n+1) if (h[i] < k) { if (i - a >= c) return true; else a = i; }
return false;
}
int main()
{
scanf("%d%d", &n, &c);
rep(i, 1, n) scanf("%d", &s[i]);
rep(i, 1, n) rk[i] = s[i];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p*=2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a] == s[i+a]) a++; h[rk[i]] = a;
}
l = 0, r = n+1; while (l!=r) if (Check((l+r)/2+1)) l = (l+r)/2+1; else r = (l+r)/2;
printf("%d
", l);
return 0;
}
SPOJ DISUBSTR: Distinct Substrings
求不相同·子串的个数。
Ans=∑(n-sa[k]+1-height[k])(1..k..n)
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#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 MS 2345
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int t, n, sa[MS], rk[MS], h[MS], a, p, ans;
char s[MS];
int main()
{
scanf("%d", &t);
while (t--)
{
scanf("%s", s); n = strlen(s);
rep(i, 1, n) rk[i] = s[i-1];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i; sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p*=2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a-1] == s[i+a-1]) a++; h[i] = a;
}
rep(i, 1, n) ans -= h[i];
ans += (n*n+n)/2; printf("%d
", ans); ans = 0;
}
return 0;
}
SPOJ SUBST1: New Distinct Substrings
和上一道一样咯
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#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 MS 56789
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int t, n, sa[MS], rk[MS], a, p;
long long ans;
char s[MS];
int main()
{
scanf("%d", &t);
while (t--)
{
scanf("%s", s); n = strlen(s); ans = n; ans = (ans*n+n)/2;
rep(i, 1, n) rk[i] = s[i-1];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i; sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p*=2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a-1] == s[i+a-1]) a++; ans -= a;
}
printf("%lld
", ans);
}
return 0;
}
Ural 1297: Palindrome
最长回文子串,经典!
翻转连接后就变成了求最长公共子串。
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#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 MS 2345
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int t, n, sa[MS], rk[MS], h[MS], k[MS][19], a, p, ans, ans2, x;
char s[MS];
int rmq(int x, int y)
{
if (x > y) a = x, x = y, y = a; x++;
p = int(log(y-x+1)/log(2)); a = 1; rep(i, 1, p) a *= 2;
return min(k[x][p], k[y-a+1][p]);
}
int main()
{
scanf("%s", s); n = strlen(s);
s[n] = ' '; rep(i, 1, n) s[i+n] = s[n-i]; n = n*2+1;
rep(i, 1, n) rk[i] = s[i-1];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i; sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p*=2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
}
rep(i, 1, n) k[i][0] = h[i];
a = 1; rep(j, 1, int(log(n)/log(2)))
{ rep(i, 1, n-a) k[i][j] = min(k[i][j-1], k[i+a][j-1]); a*=2; }
rep(i, 1, n/2)
{ x = rmq(rk[i], rk[n-i+1]); if (ans < x*2-1) ans = x*2-1, ans2 = i-x+1; }
rep(i, 2, n/2)
{ x = rmq(rk[i], rk[n-i+2]); if (ans < x*2) ans = x*2, ans2 = i-x; }
rep(i, ans2, ans2+ans-1) printf("%c", s[i-1]); printf("
");
return 0;
}
POJ 2406: Power Strings
给定一个字符串L,已知这个字符串是由某个字符串S重复R次而得到的,求R的最大值。
从小到大枚举S的长度K,然后判断L能否整除K,且Suf[1]与Suf[1+k]的公共前缀长度是否等于L-k
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#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 MS 1234567
#define MAX 1073741823
using namespace std;
int n, f[MS], a;
char s[MS];
int main()
{
scanf("%s", s);
while (s[0] != '.')
{
n = strlen(s);
f[0] = -1; rep(i, 2, n)
{
a = f[i-1]; while (a >= 0 && s[a] != s[i-1]) a = f[a];
if (a < 0) f[i] = 0; else f[i] = a+1;
}
if (n%(n-f[n])==0) printf("%d
", n/(n-f[n])); else printf("1
"); scanf("%s", s);
}
return 0;
}
POJ 3693: Maximum repetition substring
给定一个字符串,求重复次数最多的连续重复子串。
这题还是得看论文的解释。。。我这题貌似弄最久。。。WA了7次
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#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 MS 123456
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, rk[MS], sa[MS], h[MS], k[MS][20], rec[MS], o, m, a, p, l, T, t, ans, pd;
char s[MS];
int rmq(int x, int y)
{
int a, p;
if (x > y) a = x, x = y, y = a; x++;
a = 1, p = 0; while (a*2 <= y-x+1) a*=2, p++;
return min(k[x][p], k[y-a+1][p]);
}
int main()
{
scanf("%s", s);
while (s[0]!='#')
{
n = strlen(s);
rep(i, 1, n) rk[i] = s[i-1];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p *= 2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
}
rep(i, 1, n) k[i][0] = h[i];
a = 1, p = 0; while (a*2 <= n) a*=2, p++;
a = 1; rep(j, 1, p)
{ rep(i, 1, n-a) k[i][j] = min(k[i][j-1], k[i+a][j-1]); a*=2; }
ans = 1;
rep(l, 1, n-1)
{
a = 1;
while (a+l <= n) if (s[a-1] == s[a+l-1])
{
m = rmq(rk[a], rk[a+l]);
p = m/l+1, t = a-(l-m%l);
if (t>0 && m%l!=0 && rmq(rk[t], rk[t+l])>=m) p++;
if (p > ans) ans = p, o = 1, rec[o] = l;
else if (p == ans && rec[o] != l) rec[++o] = l;
a += l;
} else a+=l;
}
if (ans == 1) { o = n-1; rep(i, 1, o) rec[i] = i; }
pd = 0;
rep(i, 1, n) if (!pd) rep(j, 1, o)
{ if (rmq(i, rk[sa[i]+rec[j]]) >= (ans-1)*rec[j]) { a = sa[i]; p = rec[j]; pd++; break; } }
else break;
printf("Case %d: ", ++T);
rep(i, a, a+p*ans-1) printf("%c", s[i-1]); printf("
");
scanf("%s", s);
}
return 0;
}
SPOJ REPEATS: Repeats
和上一道一样咯
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#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 MS 56789
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, rk[MS], sa[MS], h[MS], k[MS][20], m, a, p, l, T, t, ans, pd;
char s[MS];
int rmq(int x, int y)
{
int a, p;
if (x > y) a = x, x = y, y = a; x++;
a = 1, p = 0; while (a*2 <= y-x+1) a*=2, p++;
return min(k[x][p], k[y-a+1][p]);
}
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
rep(i, 1, n) { s[i-1] = 0; while (s[i-1] < 'a' || s[i-1] > 'z') scanf("%c", &s[i-1]); }
rep(i, 1, n) rk[i] = s[i-1];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p *= 2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
}
rep(i, 1, n) k[i][0] = h[i];
a = 1, p = 0; while (a*2 <= n) a*=2, p++;
a = 1; rep(j, 1, p)
{ rep(i, 1, n-a) k[i][j] = min(k[i][j-1], k[i+a][j-1]); a*=2; }
ans = 1;
rep(l, 1, n-1)
{
a = 1;
while (a+l <= n) if (s[a-1] == s[a+l-1])
{
m = rmq(rk[a], rk[a+l]);
p = m/l+1, t = a-(l-m%l);
if (t>0 && m%l!=0 && rmq(rk[t], rk[t+l])>=m) p++;
if (p > ans) ans = p;
a += l;
} else a += l;
}
printf("%d
", ans);
}
return 0;
}
Ural 1517: Freedom of Choice
最长公共子串。连起来,没了。
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <cmath>
#include <algorithm>
#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 MS 234567
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x<k.x || (x==k.x && y<k.y); }
} q[MS];
int n, l, a, p, rk[MS], sa[MS], h[MS], ans;
char s[MS], s1[MS];
int main()
{
scanf("%d", &l);
scanf("%s", s1); rep(i, 1, l) s[i-1] = s1[i-1]; s[l] = ' '; n = l+1;
scanf("%s", s1); rep(i, 1, l) s[n+i-1] = s1[i-1]; n += l;
rep(i, 1, n) rk[i] = s[i-1];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p *= 2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
}
l = sa[1]; rep(i, 3, n) if ((sa[i-1] < l && sa[i] > l) || (sa[i-1] > l && sa[i] < l)) ans = max(ans, h[i]);
rep(i, 3, n) if (((sa[i-1] < l && sa[i] > l) || (sa[i-1] > l && sa[i] < l)) && ans == h[i])
{
rep(j, 1, ans) printf("%c", s[sa[i]+j-2]);
return 0;
}
return 0;
}
POJ 2774: Long Long Message
也是最长公共子串。
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <cmath>
#include <algorithm>
#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 MS 234567
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x<k.x || (x==k.x && y<k.y); }
} q[MS];
int n, l, a, p, rk[MS], sa[MS], h[MS], ans;
char s[MS], s1[MS];
int main()
{
scanf("%s", s1); l = strlen(s1);
rep(i, 1, l) s[i-1] = s1[i-1]; s[l] = ' '; n = l+1;
scanf("%s", s1); l = strlen(s1);
rep(i, 1, l) s[n+i-1] = s1[i-1]; n += l;
rep(i, 1, n) rk[i] = s[i-1];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p *= 2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
}
l = sa[1]; rep(i, 3, n) if ((sa[i-1] < l && sa[i] > l) || (sa[i-1] > l && sa[i] < l)) ans = max(ans, h[i]);
printf("%d", ans);
return 0;
}
POJ 3415: Common Substrings
从上到下遍历Height,分组讨论。对于每个组,答案加上组内来自不同串的后缀两两之间的公共前缀长度之和。这一步得用单调栈维护才能够比较快。。。
#include <cstdio>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#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 MS 234567
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, k, l, la, rk[MS], sa[MS], h[MS], a, p, b;
long long ans, as, bs;
char s[MS], s1[MS];
int am, bm;
struct node2 {int x, y;} ac[MS], bc[MS];
int main()
{
scanf("%d", &k);
while (k)
{
scanf("%s", s1); l = strlen(s1); rep(i, 1, l) s[i-1] = s1[i-1]; s[l] = ' '; n = la = l+1;
scanf("%s", s1); l = strlen(s1); rep(i, 1, l) s[n+i-1] = s1[i-1]; n += l;
rep(i, 1, n) rk[i] = s[i-1];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n ? 0 : rk[i+p], q[i].z = i; sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p *= 2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
}
ans = am = bm = h[1] = 0;
rep(i, 1, n) h[i] += 1-k;
rep(i, 1, n)
{
if (h[i] < 1) am = bm = as = bs = 0;
a = am; while (a && ac[a].x > h[i])
{
as -= (ac[a].x - h[i])*ac[a].y, ac[a].x = h[i]; if (a != am) ac[a].y += ac[a+1].y, am--;
a--;
}
b = bm; while (b && bc[b].x > h[i])
{
bs -= (bc[b].x - h[i])*bc[b].y, bc[b].x = h[i]; if (b != bm) bc[b].y += bc[b+1].y, bm--;
b--;
}
if (sa[i] > la)
bc[++bm].x = MAX, bc[bm].y = 1, bs += MAX, ans += as;
else
ac[++am].x = MAX, ac[am].y = 1, as += MAX, ans += bs;
}
printf("%lld
", ans); scanf("%d", &k);
}
return 0;
}
SPOJ PHRASES: Relevant Phrases of Annihilation
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstdio>
#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 MS 123456
#define MAX 1073741823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, m, l, r, a, p, rk[MS], sa[MS], h[MS], b[MS], c[MS], d[MS];
char s1[MS], s[MS];
bool Query(int k)
{
a = p = 0;
rep(i, 1, n)
{
if (h[i] < k) a = 0, p++;
if (b[sa[i]] && c[b[sa[i]]] != p) c[b[sa[i]]] = p, a++;
if (a > m/2) return true;
}
return false;
}
int main()
{
scanf("%d", &m);
while (m)
{
n = 0, r = MAX;
rep(i, 1, m)
{
scanf("%s", s1); l = strlen(s1); r = min(r, l);
rep(j, 1, l) s[n+j-1] = s1[j-1], b[n+j] = i, d[n+j] = l-j+1; if (i<m) s[n+l] = ' ', n += l+1; else n += l;
}
rep(i, 1, n) rk[i] = s[i-1];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i;
sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p *= 2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
}
h[1] = 0; rep(i, 2, n) if (min(d[sa[i-1]], d[sa[i]]) < h[i]) h[i] = min(d[sa[i-1]], d[sa[i]]);
l = 0;
while (l != r)
if (Query((l+r)/2+1)) l = (l+r)/2+1; else r = (l+r)/2;
if (!l) printf("?
"); else
{
a = p = 0;
rep(i, 1, n)
{
if (h[i] < l) a = 0, p++;
if (b[sa[i]] && c[b[sa[i]]] != p) c[b[sa[i]]] = p, a++;
if (a == m/2+1) { a = -MAX; rep(j, sa[i]-1, sa[i]+l-2) printf("%c", s[j]); printf("
"); }
}
}
printf("
"); scanf("%d", &m);
}
return 0;
}
POJ 3294: Life Forms
#include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> #include <fstream> #include <cstdio> #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 MS 123456 #define MAX 1073741823 using namespace std; struct node { int x, y, z; bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); } } q[MS]; int n, m, l, r, a, p, rk[MS], sa[MS], h[MS], b[MS], c[MS], d[MS]; char s1[MS], s[MS]; bool Query(int k) { a = p = 0; rep(i, 1, n) { if (h[i] < k) a = 0, p++; if (b[sa[i]] && c[b[sa[i]]] != p) c[b[sa[i]]] = p, a++; if (a > m/2) return true; } return false; } int main() { scanf("%d", &m); while (m) { n = 0, r = MAX; rep(i, 1, m) { scanf("%s", s1); l = strlen(s1); r = min(r, l); rep(j, 1, l) s[n+j-1] = s1[j-1], b[n+j] = i, d[n+j] = l-j+1; if (i<m) s[n+l] = ' ', n += l+1; else n += l; } rep(i, 1, n) rk[i] = s[i-1]; p = 1; while (p <= n) { rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i; sort(q+1, q+1+n); a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a; if (a == n) break; else p *= 2; } rep(i, 1, n) sa[rk[i]] = i; a = 0; rep(i, 1, n) { if (a) a--; p = sa[rk[i]-1]; while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a; } h[1] = 0; rep(i, 2, n) if (min(d[sa[i-1]], d[sa[i]]) < h[i]) h[i] = min(d[sa[i-1]], d[sa[i]]); l = 0; while (l != r) if (Query((l+r)/2+1)) l = (l+r)/2+1; else r = (l+r)/2; if (!l) printf("? "); else { a = p = 0; rep(i, 1, n) { if (h[i] < l) a = 0, p++; if (b[sa[i]] && c[b[sa[i]]] != p) c[b[sa[i]]] = p, a++; if (a == m/2+1) { a = -MAX; rep(j, sa[i]-1, sa[i]+l-2) printf("%c", s[j]); printf(" "); } } } printf(" "); scanf("%d", &m); } return 0; }
POJ 1226: Substrings
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <fstream>
#define MS 123456
#define MAX 1<<30
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return x < k.x || (x == k.x && y < k.y); }
} q[MS];
int n, T, m, a, p, l, r, rk[MS], sa[MS], h[MS], b[MS], d[MS], c[MS];
char s1[MS], s[MS];
bool Query(int k)
{
a = p = 0; rep(i, 1, m) c[i] = 0;
rep(i, 1, n)
{
if (h[i] < k) a = 0, p++;
if (b[sa[i]] && p != c[b[sa[i]]]) c[b[sa[i]]] = p, a++;
if (a == m) return true;
}
return false;
}
int main()
{
scanf("%d", &T);
while (T--)
{
scanf("%d", &m); n = 0, r = MAX;
rep(i, 1, m)
{
scanf("%s", s1); l = strlen(s1); r = min(l, r);
rep(j, 1, l) s[n+j-1] = s1[j-1], b[n+j] = i, d[n+j] = l-j+1; n += l+1; s[n-1] = ' ', b[n] = 0, d[n] = 0;
rep(j, 1, l) s[n+j-1] = s1[l-j], b[n+j] = i, d[n+j] = l-j+1; n += l+1; s[n-1] = ' ', b[n] = 0, d[n] = 0;
}
n--; rep(i, 1, n) rk[i] = s[i-1];
p = 1; while (p <= n)
{
rep(i, 1, n) q[i].x = rk[i], q[i].y = i+p>n?0:rk[i+p], q[i].z = i;
sort(q+1, q+1+n);
a = 0; rep(i, 1, n) if (q[i].x != q[i-1].x || q[i].y != q[i-1].y) rk[q[i].z] = ++a; else rk[q[i].z] = a;
if (a == n) break; else p *= 2;
}
rep(i, 1, n) sa[rk[i]] = i;
a = 0; rep(i, 1, n)
{
if (a) a--; p = sa[rk[i]-1];
while (s[p+a-1] == s[i+a-1]) a++; h[rk[i]] = a;
}
h[1] = 0; rep(i, 2, n) if (min(d[sa[i-1]], d[sa[i]]) < h[i]) h[i] = min(d[sa[i-1]], d[sa[i]]);
l = 0; while (l != r) if (Query((l+r)/2+1)) l = (l+r)/2+1; else r = (l+r)/2;
printf("%d
", l);
}
return 0;
}
上面三题都是二分答案+分组讨论,也没什么好说了。
总计18道。。。
累。。。