主要参考:
http://hi.baidu.com/ahnkftravhdhkyr/item/cc38703dd46547cd392ffab1
及cxlove博客
主要是论文《后缀数组——处理字符串的有力工具》一些题解和其它题目的主要题解
一般的模板:
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#define REP(i, n) for(int i=0; i<n; i++)
#define CLR(a, b) memset(a, b, sizeof(a))
#define PB push_back
#define LL long long
using namespace std;
const int MAXN = 1010 * 100;
const int INF = 0x3f3f3f3f;
int wa[MAXN], wb[MAXN], wv[MAXN], wn[MAXN];
char r[MAXN];
int a[MAXN], sa[MAXN], rank[MAXN], height[MAXN];
int cmp(int *r, int a,int b, int k)
{
return r[a] == r[b] && r[a + k] == r[b + k];
}
void build_sa(int *r, int *sa, int n,int m)
{
int i,j, p;
int *x = wa, *y = wb, *t;
for (int i= 0; i < m; i++) wn[i] = 0;
for (int i= 0; i < n; i++) wn[x[i] = r[i]]++;
for (int i = 1; i < m; i++) wn[i] += wn[i - 1];
for (int i = n - 1; i >= 0; i--) sa[--wn[x[i]]] = i;
for (p = 1, j = 1; p < n; j <<= 1, m = p)
{
for (p = 0, i = n - j; i < n; i++) y[p++] = i;
for (i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i] - j;
for (i = 0; i < m; i++) wn[i] = 0;
for (i = 0; i < n; i++) wn[wv[i] = x[y[i]]]++;
for (i = 1; i < m; i++) wn[i] += wn[i - 1];
for (i = n - 1; i >= 0; i--) sa[--wn[wv[i]]] = y[i];
t = x; x = y; y = t;
x[sa[0]] = 0; p = 1;
for (i = 1; i < n; i++)
x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
}
}
void getHeight(int *r, int *sa, int n)
{
int i, j, k = 0;
for (i = 1; i <= n; i++)
{
rank[sa[i]] = i;
height[i] = 0;
}
for (i = 0;i < n; i++)
{
if (k) k--;
j = sa[rank[i] - 1];
while (r[i + k] == r[j + k]) k++;
height[rank[i]] = k;
}
}
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;
}
map<int, int>M;
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<stdio.h>
#include<cstring>
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)<tb?(x)*3+1:((x)-tb)*3+2)
int c0(int *r,int a,int b)
{return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];}
int c12(int k,int *r,int a,int b)
{if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];}
void sort(int *r,int *a,int *b,int n,int m)
{
int i;
for(i=0;i<n;i++) wv[i]=r[a[i]];
for(i=0;i<m;i++) ws[i]=0;
for(i=0;i<n;i++) ws[wv[i]]++;
for(i=1;i<m;i++) ws[i]+=ws[i-1];
for(i=n-1;i>=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<n;i++) if(i%3!=0) wa[tbc++]=i;
sort(r+2,wa,wb,tbc,m);
sort(r+1,wb,wa,tbc,m);
sort(r,wa,wb,tbc,m);
for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
if(p<tbc) dc3(rn,san,tbc,p);
else for(i=0;i<tbc;i++) san[rn[i]]=i;
for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
if(n%3==1) wb[ta++]=n-1;
sort(r,wb,wa,ta,m);
for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
for(i=0,j=0,p=0;i<ta && j<tbc;p++)
sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
for(;i<ta;p++) sa[p]=wa[i++];
for(;j<tbc;p++) sa[p]=wb[j++];
return;
}
void cal(int *r,int *sa,int n){
int i,j,k=0;
for (i=1;i<=n;i++) rank[sa[i]]=i;
for (i=0;i<n;height[rank[i++]]=k)
for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
return;
}
void rmq(int *height,int n){
int i,j=rank[0],min=n-sa[j];
for (i=j;i>=1;i--){
f[i]=min;
min=min<height[i]?min:height[i];
}
for (i=j+1,min=1000000;i<=n;i++){
min=min<height[i]?min:height[i];
f[i]=min;
}
return;
}
int work(int n){
int i;
for (i=0;i<n;i++)
if (n%(i+1)==0)
if (f[rank[i+1]]==n-i-1) return n/(i+1);
return 0;
}
int main(){
while (scanf("%s",s)!=EOF){
if (strcmp(s,".")==0) return 0;
int n=strlen(s),i;
for (i=0;i<n;i++) a[i]=static_cast<int>(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<pair<int, int> > 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]<L&& sa[i-1]<L)
if(pos[sa[i-1]]<height[i])
pos[sa[i-1]]=height[i];
LL ans = (LL) L * (L + 1) / 2;
for (int i = 0; i < L; i++) ans -= pos[i];
printf("Case %d: %I64d\n", ncase++ , ans);
}
int main()
{
int T;
int n2;
RI(T);
ncase = 1;
while (T--)
{
RI(n2);
RS(r);
L = strlen(r);
m = 256;
n = 0;
REP(i, L) a[n++] = r[i];
a[n++] = m++;
REP(i, n2)
{
RS(r);
int l = strlen(r);
REP(j, l)
a[n++] = r[j];
a[n++] = m++;
}
a[--n] = 0;
build_sa(a, sa, n + 1, m);
getHeight(a, sa, n);
solve();
}
return 0;
}
/**
题目:给出两个串,找出两个串的后缀的LCP恰好为K的有多少对。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;
}