虽然不能理解,但好歹做个模板,方便以后copy23333
#include <bits/stdc++.h>
using namespace std;
/*
1.判断一个串s是否为p的后缀
2.多模式串匹配
3.求原串的后缀的最长公共前缀\
*/
const int maxn=10000+10;
char s[maxn];//原串
int sa[maxn],t[maxn],t2[maxn],c[maxn],n;//sa就是后缀数组
//构造后缀数组
int Rank[maxn],height[maxn];//rank:后缀在sa数组中的下标,height:sa[i-1]和sa[i]的最长公共前缀
//预处理height
void getHeight(){
int i,j,k = 0;
for(i = 0; i < n; i++) Rank[sa[i]] = i;
for(i = 0; i < n; i++)
{
if(k) k--;
j = sa[Rank[i]-1];
while(s[i+k] == s[j+k]) k++;
height[Rank[i]] = k;
}
}
void build_sa(int m){
int i,*x=t,*y=t2;
//基数排序
for(int i=0;i<m;i++)c[i]=0;
for(int i=0;i<n;i++)c[x[i]=s[i]]++;
for(int i=1;i<m;i++)c[i]+=c[i-1];
for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
for(int k=1;k<=n;k++){
int p=0;
//利用sa数组排序第二关键字
for(int i=n-k;i<n;i++)y[p++]=i;
for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
//基数排序第一关键字
for(int i=0;i<m;i++)c[i]=0;
for(int i=0;i<n;i++)c[x[y[i]]]++;
for(int i=0;i<m;i++)c[i]+=c[i-1];
for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<n;i++){
x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p+1;
}
if(p>=n)break;//以后即使继续倍增,也不会改变sa
m=p;//下次基数排序的最大值
}
getHeight();//在这里调用,预处理height数组
}
int m;
//找出串p在原串中的一个位置
int cmp_suffix(char *pattern,int p){
return strncmp(pattern,s+sa[p],m);
}
int find(char *p){
m=strlen(p);
if(cmp_suffix(p,0)<0)return -1;
if(cmp_suffix(p,n-1)>0)return -1;
int L=0,R=n-1;
while(R>=L){//二分查找
int M=L+(R-L)/2;
int res=cmp_suffix(p,M);
if(!res)return M;
if(res<0)R=M-1;
else L=M+1;
}
return -1;//找不到
}
/*
前面处理完height数组,还需要RMQ预处理和查询
*/
int RMQ[maxn], mm[maxn], best[20][maxn];
//预处理
void initRMQ(int n)
{
mm[0] = -1;
for(int i = 1; i <= n; i++)
mm[i] = ((i&(i-1))==0) ? mm[i-1]+1 : mm[i-1];
for(int i = 1; i <= n; i++) best[0][i] = i;
for(int i = 1; i <= mm[n]; i++)
for(int j = 1; j+(1<<i)-1 <= n; j++)
{
int a = best[i-1][j];
int b = best[i-1][j + (1<<(i-1))];
if(RMQ[a] < RMQ[b]) best[i][j] = a;
else best[i][j] = b;
}
}
//查询后缀a,b的LCP
int askRMQ(int a, int b)
{
int t;
t = mm[b-a+1];
b -= (1<<t)-1;
a = best[t][a]; b = best[t][b];
return RMQ[a] < RMQ[b] ? a : b;
}
//查询后缀a,b的LCP
int lcp(int a, int b)
{
a = Rank[a], b = Rank[b];
if(a > b) swap(a, b);
return height[askRMQ(a+1, b)];
}
//找出一个重复出现k次的子串的个数,后缀数组+lcp+二分
bool judge(int l, int k, int n)
{
int cnt = 0;
for(int i = 2; i <= n; i++)
{
if(height[i] < l) cnt = 0;
else {
cnt++;
if(cnt+1 >= k) return true;
}
}
return false;
}
/**
给你一个长为N的字符串,求不同的子串的个数?
对于一个后缀sa[i],它产生了n-sa[i]个前缀,减去height[i]个相同的前缀(与前一个比较),
则产生了n-sa[i]-height[i]个子串。累加后即结果。
*/
void solve() {
long long ans = 0;
for(int i = 1; i <= n; i++ ) {
ans += n + 1 - sa[i] - height[i];
}
cout << ans << endl;
}
/*
类似的有,给定n个串,求至少出现在k个串中的最长字串
这个问题和;蓝书上P223的UVA11107相似,可以参考此题
*/