主要参考:
http://hi.baidu.com/ahnkftravhdhkyr/item/cc38703dd46547cd392ffab1
及cxlove博客
主要是论文《后缀数组——处理字符串的有力工具》一些题解和其它题目的主要题解
新模板:UVA11107 后缀数组(new模板)
一般的模板:
#include
#include
#include
#include
#include
#include
#include
#include
#include
debuge:
debuge:
0.debuge
void debuge_sa()
{
for (int i = 0; i <= n; i++)
cout << i << ' ' << sa[i] << ' ' << height[i] << endl;
cout << "^^^^^^^^^^^^^^" << endl << endl;
for (int i = 0; i <= n; i++)
cout << i << ' ' << rank[i] << endl;
cout << "^^^^^^^^^^^^^^" << endl << endl;
init_rmq(n);
for (int i = 1; i <= n; i++)
{
for (int j = i; j <= n; j++)
printf("%d ", rmq(i, j));
printf("\n");
}
}
/*
debuge_sa:
ABABABAB
0 8 0
1 6 0
2 4 2
3 2 4
4 0 6
5 7 0
6 5 1
7 3 3
8 1 5
^^^^^^^^^^^^^^
0 4
1 8
2 3
3 7
4 2
5 6
6 1
7 5
8 0
^^^^^^^^^^^^^^
0 2 2 2 0 0 0 0
2 4 4 0 0 0 0
4 6 0 0 0 0
0 0 0 0 0
0 1 1 1
1 3 3
3 5
0
*/
一些论文上的题目:
//1、求单个子串的不重复子串个数。SPOJ 694
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int main()
{
int t,i;
int n, m;
cin >> t;
while (t--)
{
RS(r);
n = strlen(r);
m = 256;
REP(i, n)
a[i] = r[i];
a[n] = 0;
build_sa(a, sa, n + 1, m);
getHeight(a, sa, n);
int ans = 0;
FE(i, 1, n) ans += n - sa[i] - height[i];
cout << ans << endl;
}
return 0;
}
/**
4、最长重复不重叠子串 PKU1743
题意:给出一个旋律,用n个数字[1,88]表示其音符,问它最长的主题长度是多少。
一个旋律的主题是一段至少出现过两次的不重叠音乐片段。
所谓重复出现,包括一段音乐全体加上某个数后再次出现。
如1 2 3 4 5和5 6 7 8 9是同一个音乐片段。主题长度至少为5.无解输出0。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
bool check(int L, int n)
{
int smin = sa[1];
int smax = sa[1];
for (int i = 2; i <= n; i++)
{
if (height[i] < L)
{
if (smax - smin >= L) return 1;
smin = smax = sa[i];
}
else
{
smin = min(smin, sa[i]);
smax = max(smax, sa[i]);
}
}
// if (height[n] >= L && smax - smin >= L)///
// return 1;
return 0;
}
void solve(int n)
{
int l = 0;
int r = n / 2;
while (l <= r)
{
int m = (l + r) >> 1;
if (check(m, n)) l = m + 1;
else r = m - 1;
}
WI(l);
}
int x[MAXN];
int main()
{
int t,i;
int n, m;
while (~RI(n) && n)
{
REP(i, n) RI(x[i]);
a[0] = 500;
for (int i = 1; i < n; i++) a[i] = x[i] - x[i - 1] + 100;
a[n] = 0;
m = 501;
build_sa(a, sa, n + 1, m);
getHeight(a, sa, n);
solve(n);
}
return 0;
}
/**
5、最长的出现k次的重复(可重叠)子串。 PKU3261
题目大意:给出n个数字组成的一个字符串,求最长的恰好出现k次的重复子串(可重叠)的字符串的长度。
分析:后缀数组一个经典的应用。先二分答案,然后分组。只要某一组包含的后缀数量大于等于k,表示有解。这个不难理解。等完成了论文里面的练习之后,我再写个总结笔记吧。
深刻体会到后缀数组的强大....
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
char r[MAXN];
int n;
int K;
bool check(int k)
{
int num = 1;
FE(i, 2, n)
{
if (height[i] < k)
{
if (num >= K) return 1;
num = 1;
}
else
num++;
}
if (num >= k) return 1;
return 0;
}
void solve()
{
int l = 0, r = n, mid;
while (l <= r)
{
mid = (l + r) >> 1;
if (check(mid)) l = mid + 1;
else r = mid - 1;
}
cout << r << endl;
}
mapM;
int main()
{
int x[MAXN];
int T;
while (~RII(n, K) && n)
{
M.clear();
int MM = 1;
REP(i, n)
{
RI(x[i]);
if (M.count(x[i]) == 0) M[x[i]] = MM++;
}
REP(i, n) a[i] = M[x[i]];
a[n]= 0;
build_sa(a, sa, n + 1, MM + 5);
getHeight(a, sa, n);
solve();
}
}
/***
7.求一个串最多由哪个串复制若干次得到 PKU2406 (!!!此代码是网上的)
题目大意:给出一个字符串s,则存在子串a,a重复k次后得到s。求k的最大值。
*/
#include
#include
using namespace std;
const int maxn=3000010;
int ws[maxn],wa[maxn],wb[maxn],wv[maxn],sa[maxn],rank[maxn],height[maxn],a[maxn],f[maxn];
char s[maxn];
//dc3
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)=0;i--) b[--ws[wv[i]]]=a[i];
return;
}
void dc3(int *r,int *sa,int n,int m)
{
int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
r[n]=r[n+1]=0;
for(i=0;i=1;i--){
f[i]=min;
min=min(s[i]);
a[n]=0;
dc3(a,sa,n+1,123);
cal(a,sa,n);
rmq(height,n);
printf("%d\n",work(n));
}
return 0;
}
/***
8.最长公共子串 Pku2774--Long Long Message
题目大意:给出两个字符串,求它们的最长公共子串的长度。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
char r1[MAXN], r2[MAXN];
int nr1, nr2;
bool check(int x, int y)
{
if (x > y) swap(x, y);
if (x < nr1 && y > nr1) return 1;
else return 0;
}
void solve()
{
int ans = 0;
FE(i, 2, nr1 + nr2 + 1)
{
if (check(sa[i], sa[i - 1]))
{
if (height[i] > ans) ans = height[i];
}
}
cout << ans << endl;
}
int main()
{
while (scanf("%s%s", r1, r2) != EOF)
{
nr1 = strlen(r1);
nr2 = strlen(r2);
REP(i, nr1) a[i] = (int)r1[i];
a[nr1] = 1;
REP(i, nr2) a[i + nr1 + 1] = (int)r2[i];
a[nr1 + nr2 + 2] = 0;
build_sa(a, sa, nr1 + nr2 + 2, 256);
getHeight(a, sa, nr1 + nr2);
solve();
}
}
/**
9.重复次数最多的重复子串 SPOJ687--Repeats
题目大意:给出一个字符串,求该字符串中出现次数最多的连续重复子串。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int h[MAXN][20];
int init_rmq(int n)
{
FE(i, 1, n)
h[i][0] = height[i];
for (int j = 1; (1 << j) <= n; j++)
{
for (int i = 1; i + (1 << j) - 1 <= n; i++)
{
h[i][j] = min(h[i][j - 1], h[i + (1 << (j - 1))][j - 1]);
}
}
}
int rmq(int L, int R)///要求L!=R 且 L>=0 , R >= 0
{
L = rank[L];
R = rank[R];
if (L > R) swap(L, R);
L++;
int k = 0;
while ((1 << (k + 1)) <= (R - L + 1)) k++;
return min(h[L][k], h[R - (1 << k) + 1][k]);
}
int getnum(int L, int n)
{
int ans = 1;
for (int i = 0; i < n - L; i += L)
{
int dd = rmq(i, i + L);
if (i && dd % L)
{
dd = L - (dd % L);
dd = rmq(i - dd, i + L - dd);
}
ans = max(ans, dd / L + 1);
}
return ans;
}
void solve(int n)
{
int ans = 1;
FE(i, 1, n - 1)
{
ans = max(ans, getnum(i, n));
}
WI(ans);
}
int main()
{
int t,i;
int n, m;
int T;
RI(T);
while (T--)
{
RI(n);
REP(i, n) scanf(" %c", &r[i]);
REP(i, n ) a[i] = r[i];
a[n] = 0;
m = 256;
build_sa(a, sa, n + 1, m);
getHeight(a, sa, n);
init_rmq(n);
solve(n);
}
return 0;
}
/**
10.多个串的公共子串问题 PKU3294
[后缀数组]Pku3294--Life Forms
题目大意:给出n个字符串,求一个最长的串,它出现在一半的串以上。若要多个,按字典顺序输出。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
//int h[MAXN][30];
int idx[MAXN];
inline int getid(int x)
{
return idx[sa[x]];
}
bool check(int k, int t, int n)
{
int vis[110];///!!!
CLR(vis, 0);
int num = (t >> 1) + 1;
int step = 1;///
int x = getid(1);///
vis[x] = 1;///
int tol = 1;///
FE(i, 2, n)
{
if (height[i] < k)
{
if (tol >= num) return 1;
step++;
x = getid(i);
vis[x] = step;
tol = 1;
}
else
{
x = getid(i);
if (vis[x] != step)
{
vis[x] = step;
tol++;
}
}
}
if (tol >= num && height[n - 1] >= k) return 1;
return 0;
}
void print(int k, int t, int n)
{
int vis[110];///!!!
CLR(vis, 0);
int num = (t >> 1) + 1;
int step = 1;///
int x = idx[sa[1]];///
vis[x] = 1;///
int tol = 1;///
FE(i, 2, n)
{
if (height[i] < k)
{
if (tol >= num)
{
int x = sa[i - 1];///!!!
for (int j = x; j < x + k; j++)
printf("%c", (char)a[j]);
printf("\n");
}
step++;
x = idx[sa[i]];
vis[x] = step;
tol = 1;
}
else
{
x =idx[sa[i]];
if (vis[x] != step)
{
vis[x] = step;
tol++;
}
}
}
if (tol >= num && height[n - 1] >= k)///!!!
{
int x = sa[n - 1];
for (int j = x; j < x + k; j++)
printf("%c", (char)a[j]);
printf("\n");
}
}
void solve(int t, int n)
{
int l = 0, r = 1010, mid;
while(l <= r)
{
mid = (l + r) >> 1;
if (check(mid, t, n)) l = mid + 1;
else r = mid - 1;
}
if (r == 0) printf("?\n");
else print(r, t, n);
printf("\n");
}
int main()
{
int t,i;
int l, n, m;
int ncase = 1;
while (cin >> t && t)
{
n = 0;
m = 256;
CLR(idx, 0);///!!!
FE(i, 1, t)
{
RS(r);
l = strlen(r);
REP(j, l)
{
a[n] = (int)r[j];
idx[n++] = i;
}
a[n++] = m++;
}
a[--n] = 0;
build_sa(a,sa,n+1,m);
getHeight(a,sa,n);
solve(t, n);
}
return 0;
}
/**
11、出现或反转后出现所有字符串中的最长子串 Pku1226--Substrings
题目大意:给出n个串,求一个最长的串x,它或者它的反串出现在所有的串中。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int idx[MAXN];
int getid(int x)
{
return idx[sa[x]];
}
bool check(int l, int t, int n)
{
int vis[110], step, tol;
CLR(vis, 0);
step = 1;
int x = getid(1);
vis[x] = step;
tol = 1;
FE(i, 2, n)
{
if (height[i] < l)
{
CLR(vis, 0);
++step;
x = getid(i);
vis[x] = step;
tol = 1;
}
else
{
x = getid(i);
if (vis[x] != step)
{
vis[x] = step;
++tol;
}
if (tol >= t) return 1;
}
}
return 0;
}
void solve(int t, int n)
{
int l = 0, r = 100;
while (l <= r)
{
int mid = (l + r) >> 1;
if (check(mid, t, n)) l = mid + 1;
else r = mid - 1;
}
cout << r << endl;
}
int main()
{
int T;
cin >> T;
int t, n, m;
while (T--)
{
RI(t);
CLR(idx, 0);
n = 0;
m = 256;
FE(i, 1, t)///1
{
RS(r);
int l = strlen(r);
REP(j, l)
{
a[n] = (int)r[j];
idx[n++] = i;
}
a[n++] = m++;
for (int j = l - 1; j >= 0; j--)
{
a[n] = (int)r[j];
idx[n++] = i;
}
a[n++] = m++;
}
a[--n] = 0; --m;
if (t == 1)
{
cout << strlen(r) << endl;
continue;
}
build_sa(a, sa, n + 1, m);
get_height(a, sa, n);
solve(t, n);
}
return 0;
}
/**
12、不重叠地至少两次出现在所有字符串中的最长子串 spoj220
题目大意:给出n个字符串,求一个最长的串x,x在每个字符串中不重叠出现至少两次。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int idx[MAXN];
int ns;
int n, m;
int lmax;
int used[12];
int smin[12], smax[12];
int getid(int x)
{
return idx[sa[x]];
}
void clear_up()
{
REP(i, 12) smin[i] = MAXN;
REP(i, 12) smax[i] = -1;
}
bool check(int L)
{
clear_up();
smin[getid(1)] = sa[1];
smax[getid(1)] = sa[1];
FE(i, 2, n)
{
int x = getid(i);
int y = sa[i];
if (height[i] < L)
{
int tol = 0;
REP(i, ns)
if (smax[i] - smin[i] >= L) tol++;
if (tol == ns) return 1;
clear_up();
smin[x] = smax[x] = y;
}
else
{
smin[x] = min(smin[x], y);
smax[x] = max(smax[x], y);
}
}
if (height[n] >= L)
{
int tol = 0;
REP(i, ns)
if (smax[i] - smin[i] >= L) tol++;
if (tol == ns) return 1;
}
return 0;
}
void solve()
{
int l = 0;
int r = lmax;
while (l <= r)
{
int m = (l + r) >> 1;
if (check(m)) l = m + 1;
else r = m - 1;
}
WI(r);
}
int main()
{
int T;
RI(T);
while (T--)
{
RI(ns);
CLR(idx, 0);
m = 256;
n = 0;
lmax = -1;
REP(i, ns)
{
RS(r);
int l = strlen(r);
if (lmax == -1 || lmax > l) lmax = l;
REP(j, l)
{
a[n] = r[j];
idx[n++] = i;
}
a[n++] = m++;
// idx[n++] = 0;
}
a[--n] = 0;
build_sa(a, sa, n + 1, m);
getHeight(a, sa, n);
solve();
}
return 0;
}
/**
13.两个字符串的重复子串个数。 Pku3415--Common Substrings
题目大意:给出两个字符串,求在这两个字符串中出现次数不少于k的公共子串的个数(可以重复)。
例如xx和xx这两个字符串,假如k=1,则有5个。(4个位置不同的“x”,和一个“xx”)。
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int idx[MAXN];
int h[MAXN][30];
int n1, n2, n, m, k;
int getid(int x)
{
return idx[sa[x]];
}
stack > S;
LL getnum(int l, int r, int op)
{
LL ret = 0;
LL now = 0;
while (!S.empty()) S.pop();
FE(i, l, r - 1)
{
int c = getid(i);
if (c == op) ret += now;
else now += height[i + 1] - k + 1;
int xn = (c == op ? 0 : 1);
int xs = 0;
while (!S.empty() && S.top().first >= height[i + 1])
{
xn += S.top().second;
now -= 1LL * S.top().second * (S.top().first - height[i + 1]);
S.pop();
}
if (xn) S.push(make_pair(height[i + 1], xn));
}
if (getid(r) == op) ret += now;
return ret;
}
void solve()
{
LL ans = 0;
LL last = 1;
FE(i, 2, n)
{
if (height[i] < k)
{
ans += getnum(last, i - 1, 1) + getnum(last, i - 1, 2);
last = i;
}
}
if (height[n] >= k) ans += getnum(last, n, 1) + getnum(last, n, 2);
cout << ans << endl;
}
int main()
{
int x;
while (cin >> k && k)
{
CLR(idx, 0);
n = 0;
m = 256;
RS(r);
n1 = strlen(r);
REP(i, n1)
{
a[n] = r[i];
idx[n++] = 1;
}
a[n++] = m++;
RS(r);
n2 = strlen(r);
REP(i, n2)
{
a[n] = r[i];
idx[n++] = 2;
}
a[n] = 0;
build_sa(a, sa, n + 1, m);
getHeight(a, sa, n);
solve();
}
return 0;
}
一些其他题目:
/***
输出n个串中,每个串不属于于其它串的最小子串 URAL 1713 Key Substrings 后缀数组
*/
const int MAXN = 1010 * 100;
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int idx[MAXN];
inline int getid(int x) { return idx[sa[x]]; }
int n, m;
int ll[MAXN], rr[MAXN];///left记录sa中i位置的左边第一个不是同一串的后缀之间的lcp,rr同理。没有则值为0
void pre()
{
CLR(ll, 0); CLR(rr, 0);
for (int i = 2; i <= n; i++)
{
if (getid(i) != getid(i - 1)) ll[i] = height[i];
else ll[i] = min(ll[i - 1], height[i]);
}
for (int i = n - 1; i >= 1; i--)
{
if (getid(i) != getid(i + 1)) rr[i] = height[i + 1];
else rr[i] = min(rr[i + 1], height[i + 1]);
}
}
void solve()
{
pre();
int p[2];
p[0] = INF; p[1] = 0;
for (int i = 0; i <= n; i++)
{
if (idx[i])
{
int x = max(ll[rank[i]], rr[rank[i]]); x++;
if (idx[i + x - 1] == idx[i] && p[0] > x) p[0] = x, p[1] = i;///判断是否超过长度
}
else
{
for (int j = p[1]; j < p[1] + p[0]; j++)
printf("%c", (char)a[j]);
printf("\n");
p[0] = INF; p[1] = 0;
}
}
}
int main()
{
int t, i, l;
while (~scanf("%d", &t))
{
n = 0;
m = 256;
CLR(idx, 0);
for(int i = 1; i <= t; i++)
{
scanf("%s", r);
l = strlen(r);
REP(j, l)
{
a[n] = (int)r[j];
idx[n++] = i;
}
a[n++] = m++;
}
a[--n] = 0;
build_sa(a, sa, n + 1, m);
getHeight(a, sa, n);
solve1();
}
return 0;
}
/***
题目:给出一个A串,给出若干个B串,问A串中有多少个不同的子串不是B中的子串 hdu4416Good Article Good sentence
将所有的串拼接在一起,中间用一个不同的字符分隔开。然后求一次后缀数组以及height数组。
然后对于A中的某一个后缀,统计一下有B中的LCA有多少,就OK了,说明有A的这个后缀有LCA个子串在B中出现过。
只需要从前往后以及从后往前统计一次height就OK了。注意我们这里统计的是A与B的LCA。如果连续的两个sa是A中,那我们需要求一次最小值,保证求的是和B串的LCA。
但是题目要求的是A中的不同的子串,所以还要去重,遍历一次,如果连续两个都是A串的,则更新一下
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int ncase;
int n, m;
int L;
int pos[MAXN];
void init()
{
CLR(pos, 0);
///从前向后
int tmp = INF;
for (int i = 1; i <= n; i++)
if (sa[i] < L)
{
tmp = min(tmp, height[i]);
pos[sa[i]] = max(pos[sa[i]], tmp);
}
else tmp = INF;
///从后向前
tmp = INF;
for (int i = n; i >= 1; i--)
if (sa[i - 1] < L)
{
tmp = min(tmp, height[i]);
pos[sa[i - 1]] = max(pos[sa[i - 1]], tmp);
}
else tmp = INF;
}
void solve()
{
init();
for(int i=1;i<=n;i++)
if(sa[i]POJ 3729 Facer’s string(后缀数组)
将两个串拼接后,求出后缀数组
由于题目要求两个后缀的LCP恰好为K。
可以求出大于等于K的减去大于等于K+1的有多少个。
利用height将后缀分组,然后统计每一组内第一个串和第二个串分别有多少
*/
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int idx[MAXN];
int n1, n2, n, m, k;
int getid(int x)
{
return idx[sa[x]];
}
LL get(int L)
{
int d[3];
CLR(d, 0);
d[getid(1)] = 1;
LL ans = 0;
FE(i, 2, n)
{
if (height[i] < L)
{
if (d[2] > 0)
ans += 1LL * d[1];
CLR(d, 0);
d[getid(i)] = 1;
}
else
d[getid(i)]++;
}
if (height[n] >= L)
ans += 1LL * d[1] * d[2];
return ans;
}
void solve()
{
cout << get(k) - get(k + 1) << endl;
}
int main()
{
int x;
while (cin >> n1 >> n2 >> k)
{
CLR(idx, 0);
n = 0;
m = 10003;
REP(i, n1)
{
RI(x);
a[n] = x + 1;
idx[n++] = 1;
}
a[n++] = m++;
REP(i, n2)
{
RI(x);
a[n] = x + 1;
idx[n++] = 2;
}
a[n] = 0;
if (n == 0 || m == 0)
{
cout << 0 << endl;
continue;
}
build_sa(a, sa, n + 1, m);
getHeight(a, sa, n);
solve();
}
return 0;
}