文章目录
后缀数组中的数组意思
①
    
     
      
       
        R
       
       
        a
       
       
        n
       
       
        k
       
       
        [
       
       
        i
       
       
        ]
       
      
      
       Rank[i]
      
     
    Rank[i] 表示第 
    
     
      
       
        i
       
      
      
       i
      
     
    i 个后缀的排名是 
    
     
      
       
        R
       
       
        a
       
       
        n
       
       
        k
       
       
        [
       
       
        i
       
       
        ]
       
      
      
       Rank[i]
      
     
    Rank[i]
 ② 
    
     
      
       
        s
       
       
        a
       
       
        [
       
       
        i
       
       
        ]
       
      
      
       sa[i]
      
     
    sa[i] 表示排名为 
    
     
      
       
        i
       
      
      
       i
      
     
    i 的后缀原来是第 
    
     
      
       
        s
       
       
        a
       
       
        [
       
       
        i
       
       
        ]
       
      
      
       sa[i]
      
     
    sa[i] 个
 ③ 
    
     
      
       
        h
       
       
        e
       
       
        i
       
       
        g
       
       
        h
       
       
        t
       
       
        [
       
       
        i
       
       
        ]
       
      
      
       height[i]
      
     
    height[i] 表示排名为 
    
     
      
       
        i
       
      
      
       i
      
     
    i 和 
    
     
      
       
        i
       
       
        −
       
       
        1
       
      
      
       i-1
      
     
    i−1 的两个后缀的 
    
     
      
       
        l
       
       
        c
       
       
        p
       
      
      
       lcp
      
     
    lcp
 
    
     
      
       
        l
       
       
        c
       
       
        p
       
       
        (
       
       
        i
       
       
        ,
       
       
        j
       
       
        )
       
      
      
       lcp(i,j)
      
     
    lcp(i,j) 为排名第 
    
     
      
       
        i
       
      
      
       i
      
     
    i 到排名第 
    
     
      
       
        j
       
      
      
       j
      
     
    j 的后缀的最长公共前缀
后缀数组的一些性质
下标都是从1开始滴
 ①:排名第 
    
     
      
       
        i
       
      
      
       i
      
     
    i 的后缀的长度为:
    
     
      
       
        n
       
       
        −
       
       
        s
       
       
        a
       
       
        [
       
       
        i
       
       
        ]
       
       
        +
       
       
        1
       
      
      
       n-sa[i]+1
      
     
    n−sa[i]+1
 因为假如现在是第 
    
     
      
       
        k
       
      
      
       k
      
     
    k 个后缀,那么第 
    
     
      
       
        k
       
      
      
       k
      
     
    k 个后缀的长度是不是就是 
    
     
      
       
        n
       
       
        −
       
       
        k
       
       
        +
       
       
        1
       
      
      
       n-k+1
      
     
    n−k+1
 那现在排名第 
    
     
      
       
        i
       
      
      
       i
      
     
    i 的后缀就是原来第 
    
     
      
       
        s
       
       
        a
       
       
        [
       
       
        i
       
       
        ]
       
      
      
       sa[i]
      
     
    sa[i]个后缀,那么长度就是 
    
     
      
       
        n
       
       
        −
       
       
        s
       
       
        a
       
       
        [
       
       
        i
       
       
        ]
       
       
        +
       
       
        1
       
      
      
       n-sa[i]+1
      
     
    n−sa[i]+1
我找的这个模板下标什么的都是从1开始的,处理[1,n],所以每次会有个a[n+1]=0,T组的时候一直wa,改了这个就对了T_T
 感觉好多题目怎么用后缀数组求我都转换不过来,现在来总结一哈
hihocoder 1403 : 后缀数组一·重复旋律
题目链接:http://hihocoder.com/problemset/problem/1403?sid=1372120
 重复k次对应的就是连续k-1个height,而这连续的height 的最小值就是他们重复k次的子串的最长值
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
const int MOD=1e9+7;
int SA[maxn],Rank[maxn];
int C1[maxn],C2[maxn],A[maxn],B[maxn],tsa[maxn];//基数排序的数组,以及辅助数组
int Height[maxn];//最重要的Height数组,Height[i]表示排名第i的后缀和排名第i-1的后缀的lcp
int a[maxn];//把字符串转成数字存下来
void da(int n,int m)//n是字符串长度,m是字符串的范围
{
	for(int i=0; i<=m; i++)C1[i]=0;
	for(int i=1; i<=n; i++)C1[a[i]]++;
	for(int i=2; i<=m; i++)C1[i]+=C1[i-1];
	for(int i=n; i>=1; i--)SA[C1[a[i]]--]=i;
	Rank[SA[1]]=1;
	for(int i=2; i<=n; i++)
	{
		Rank[SA[i]]=Rank[SA[i-1]];
		if(a[SA[i]]!=a[SA[i-1]])Rank[SA[i]]++;
	}
	for(int len=1; Rank[SA[n]]<n; len<<=1)
	{
		for(int i=0; i<=n; i++)C1[i]=C2[i]=0;//这里一定是要弄到n
		for(int i=1; i<=n; i++)
		{
			A[i]=Rank[i];
			if(i+len<=n)B[i]=Rank[i+len];
			else B[i]=0;
			C1[A[i]]++;
			C2[B[i]]++;
		}
		for(int i=1; i<=n; i++)C1[i]+=C1[i-1],C2[i]+=C2[i-1];
		for(int i=n; i>=1; i--)tsa[C2[B[i]]--]=i;
		for(int i=n; i>=1; i--)SA[C1[A[tsa[i]]]--]=tsa[i];
		Rank[SA[1]]=1;
		for(int i=2; i<=n; i++)
		{
			Rank[SA[i]]=Rank[SA[i-1]];
			if(A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]])Rank[SA[i]]++;
		}
	}
	for(int i=1,h=0; i<=n; i++) //求Height数组
	{
		if(h)h--;
		while(a[i+h]==a[SA[Rank[i]-1]+h])h++;
		Height[Rank[i]]=h;
	}
}
int que[maxn];
int N,K;
int que_min(int k)//单调队列求连续k个元素的最小值
{
	int Max=0;//来求每段区间的最大值
	if(k==1)
	{
		for(int i=1;i<=N;i++)Max=max(Max,Height[i]);
		return Max;
	}
	int top=1,tail=0;
	for(int i=1;i<=N;i++)
	{
		while(i-que[top]>=k)top++;
		if(tail<top||Height[i]>=Height[que[tail]])que[++tail]=i;
		else
		{
			while(tail>=top&&Height[i]<Height[que[tail]])if(top>--tail)break;
            que[++tail]=i;
		}
		if(i>=k)Max=max(Max,Height[que[top]]);
	}
	return Max;
}
int main()
{
	while(cin>>N>>K)
	{
		for(int i=1;i<=N;i++)scanf("%d",a+i);
		da(N,100);
		cout<<que_min(K-1)<<endl;
	}
}
 
hihocoder 1407 : 后缀数组二·重复旋律2
题目链接:http://hihocoder.com/problemset/problem/1407?sid=1372226
 题目要求是:求出现次数至少为
    
     
      
       
        k
       
       
        =
       
       
        2
       
      
      
       k=2
      
     
    k=2 次的最长的不可重叠的串是多长?
 这里因为是2次重复也就是连续 k-1 个height,也就是找最连续一个最大的height嘛
 但是还要看找出的这两个height对应的串会不会重叠,所以用sa来找他们原来的原来的位置。
 因为只用看这种情况有没有,所以直接找极值看行不行
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
const int MOD=1e9+7;
int SA[maxn],Rank[maxn];
int C1[maxn],C2[maxn],A[maxn],B[maxn],tsa[maxn];//基数排序的数组,以及辅助数组
int Height[maxn];//最重要的Height数组,Height[i]表示排名第i的后缀和排名第i-1的后缀的lcp
int a[maxn];//把字符串转成数字存下来
void da(int n,int m)//n是字符串长度,m是字符串的范围
{
	for(int i=0; i<=m; i++)C1[i]=0;
	for(int i=1; i<=n; i++)C1[a[i]]++;
	for(int i=2; i<=m; i++)C1[i]+=C1[i-1];
	for(int i=n; i>=1; i--)SA[C1[a[i]]--]=i;
	Rank[SA[1]]=1;
	for(int i=2; i<=n; i++)
	{
		Rank[SA[i]]=Rank[SA[i-1]];
		if(a[SA[i]]!=a[SA[i-1]])Rank[SA[i]]++;
	}
	for(int len=1; Rank[SA[n]]<n; len<<=1)
	{
		for(int i=0; i<=n; i++)C1[i]=C2[i]=0;//这里一定是要弄到n
		for(int i=1; i<=n; i++)
		{
			A[i]=Rank[i];
			if(i+len<=n)B[i]=Rank[i+len];
			else B[i]=0;
			C1[A[i]]++;
			C2[B[i]]++;
		}
		for(int i=1; i<=n; i++)C1[i]+=C1[i-1],C2[i]+=C2[i-1];
		for(int i=n; i>=1; i--)tsa[C2[B[i]]--]=i;
		for(int i=n; i>=1; i--)SA[C1[A[tsa[i]]]--]=tsa[i];
		Rank[SA[1]]=1;
		for(int i=2; i<=n; i++)
		{
			Rank[SA[i]]=Rank[SA[i-1]];
			if(A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]])Rank[SA[i]]++;
		}
	}
	for(int i=1,h=0; i<=n; i++) //求Height数组
	{
		if(h)h--;
		while(a[i+h]==a[SA[Rank[i]-1]+h])h++;
		Height[Rank[i]]=h;
	}
}
int N;
int check(int len)
{
	int mx=0,mi=N+1;
	for(int i=1;i<=N;i++)
	{
		if(Height[i]<len)continue;
		//因为一个height是看的排第i和i-1的,所以在这两个里面找出最大位置和最小位置
		mx=max(mx,SA[i]);
		mi=min(mi,SA[i]);
		if(i-1>=1)mx=max(mx,SA[i-1]);
		if(i-1>=1)mi=min(mi,SA[i-1]);
		if(mx-mi>=len)return 1;
	}
	return 0;
}
int main()
{
	while(cin>>N)
	{
		for(int i=1;i<=N;i++)scanf("%d",a+i);
		da(N,1005);//a[i]的范围是1000,不是普通字符串的255了
		int L=1,R=N,ans;
		while(L<=R)
		{
			int mid=L+R>>1;
			if(check(mid))
			{
				ans=mid;
				L=mid+1;
			}
			else R=mid-1;
		}
		cout<<ans<<endl;
	}
}
 
hihocoder 1415 : 后缀数组三·重复旋律3
题目链接:http://hihocoder.com/problemset/problem/1415
 题意:求两个串的最长公共子串
 他是把两个串拼接到一起,然后看找height的最大值,并且height对应的两个sa[i]和sa[i-1]要在不同的两个串中就行了
同理,如果是要求k个串的最长公共子串的话,那就是找连续k-1 个height的最大值,并且还要满足每个sa都是在不同的串中
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
const int MOD=1e9+7;
int SA[maxn],Rank[maxn];
int C1[maxn],C2[maxn],A[maxn],B[maxn],tsa[maxn];//基数排序的数组,以及辅助数组
int Height[maxn];//最重要的Height数组,Height[i]表示排名第i的后缀和排名第i-1的后缀的lcp
int a[maxn];//把字符串转成数字存下来
void da(int n,int m)//n是字符串长度,m是字符串的范围
{
	for(int i=0; i<=m; i++)C1[i]=0;
	for(int i=1; i<=n; i++)C1[a[i]]++;
	for(int i=2; i<=m; i++)C1[i]+=C1[i-1];
	for(int i=n; i>=1; i--)SA[C1[a[i]]--]=i;
	Rank[SA[1]]=1;
	for(int i=2; i<=n; i++)
	{
		Rank[SA[i]]=Rank[SA[i-1]];
		if(a[SA[i]]!=a[SA[i-1]])Rank[SA[i]]++;
	}
	for(int len=1; Rank[SA[n]]<n; len<<=1)
	{
		for(int i=0; i<=n; i++)C1[i]=C2[i]=0;//这里一定是要弄到n
		for(int i=1; i<=n; i++)
		{
			A[i]=Rank[i];
			if(i+len<=n)B[i]=Rank[i+len];
			else B[i]=0;
			C1[A[i]]++;
			C2[B[i]]++;
		}
		for(int i=1; i<=n; i++)C1[i]+=C1[i-1],C2[i]+=C2[i-1];
		for(int i=n; i>=1; i--)tsa[C2[B[i]]--]=i;
		for(int i=n; i>=1; i--)SA[C1[A[tsa[i]]]--]=tsa[i];
		Rank[SA[1]]=1;
		for(int i=2; i<=n; i++)
		{
			Rank[SA[i]]=Rank[SA[i-1]];
			if(A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]])Rank[SA[i]]++;
		}
	}
	for(int i=1,h=0; i<=n; i++) //求Height数组
	{
		if(h)h--;
		while(a[i+h]==a[SA[Rank[i]-1]+h])h++;
		Height[Rank[i]]=h;
	}
}
char s[maxn];
int main()
{
	cin>>(s+1);			//第一个字符串
	int len=strlen(s+1);
	cin>>(s+len+1);		//第二个字符串
	int N=strlen(s+1);
	for(int i=1;i<=N;i++)a[i]=s[i]-'a'+1;
	da(N,300);
	int Max=0;
	for(int i=2;i<=N;i++)
	{
		int p1=SA[i-1],p2=SA[i];
		if((p1<=len&&p2>len)||(p2<=len&&p1>len))//两段能岔开就行,不管谁在前面谁在后面
		Max=max(Max,Height[i]);
	}
	cout<<Max<<endl;
}
 
hdu 6194 string string string
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6194
 题意:求恰好出现k次的子串有多少种?
这道题跟以前做的有点不一样,不是求长度啥的,而是求出现k次的子串有多少种
 比如样例:
 3
 abcabcabcabc
 出现三次的子串有
 abca
 abcab
 abcabc
 bca
 bacb
 bacbc
 ca
 cab
 cabc
以上9种都是
根据女学长的思路,我们好求的是大于等于k次的子串有多少种,然后减去大于等于k+1次的就行了,但是这个大于等于k次的要怎么求呀?
我们这样想,要找等于k次的,不就是找连续k-1个height么?假如找出的这连续k-1个height的最长公共前缀为lcp,那么至少都有lcp种
比如找出来的lcp为:abca
 那么abca是这k-1个的最长公共前缀
 abc也是
 ab也是
 a也是
 也就是说他的4个前缀都阔以,所以最少都有lcp个
 而第一个a和最后一个a又是重复的,所以说为什么这样求出来是大于等于k次的
然后就是容斥这里我感觉我好喃理解哦

 枚举长度为k的串,假如枚举到[i,j]
 那么包含长度为k的串长度为k+1的阔以是[i-1,j],也阔以是[i,j+1],
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
const int MOD=1e9+7;
int SA[maxn],Rank[maxn];
int C1[maxn],C2[maxn],A[maxn],B[maxn],tsa[maxn];//基数排序的数组,以及辅助数组
int Height[maxn];//最重要的Height数组,Height[i]表示排名第i的后缀和排名第i-1的后缀的lcp
int a[maxn];//把字符串转成数字存下来
void da(int n,int m)//n是字符串长度,m是字符串的范围 
{
	for(int i=0; i<=m; i++)C1[i]=0;
	for(int i=1; i<=n; i++)C1[a[i]]++;
	for(int i=2; i<=m; i++)C1[i]+=C1[i-1];
	for(int i=n; i>=1; i--)SA[C1[a[i]]--]=i;
	Rank[SA[1]]=1;
	for(int i=2; i<=n; i++)
	{
		Rank[SA[i]]=Rank[SA[i-1]];
		if(a[SA[i]]!=a[SA[i-1]])Rank[SA[i]]++;
	}
	for(int len=1; Rank[SA[n]]<n; len<<=1)
	{
		for(int i=0; i<=n; i++)C1[i]=C2[i]=0;//这里一定是要弄到n 
		for(int i=1; i<=n; i++)
		{
			A[i]=Rank[i];
			if(i+len<=n)B[i]=Rank[i+len];
			else B[i]=0;
			C1[A[i]]++;
			C2[B[i]]++;
		}
		for(int i=1; i<=n; i++)C1[i]+=C1[i-1],C2[i]+=C2[i-1];
		for(int i=n; i>=1; i--)tsa[C2[B[i]]--]=i;
		for(int i=n; i>=1; i--)SA[C1[A[tsa[i]]]--]=tsa[i];
		Rank[SA[1]]=1;
		for(int i=2; i<=n; i++)
		{
			Rank[SA[i]]=Rank[SA[i-1]];
			if(A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]])Rank[SA[i]]++;
		}
	}
	for(int i=1,h=0; i<=n; i++) //求Height数组
	{
		if(h)h--;
		while(a[i+h]==a[SA[Rank[i]-1]+h])h++;
		Height[Rank[i]]=h;
	}
}
int N;
char s[maxn];
int dp[maxn][30];
int lcp(int L,int R)
{
	if(L==R)return N-SA[L]+1;
	L++;//加1是因为height[i]包含了i-1的值了 
	int k=0;
	while((1<<(k+1))<R-L+1)k++;
	return min(dp[L][k],dp[R-(1<<k)+1][k]);
}
int main()
{
	int T,K;
	cin>>T;
	while(T--)
	{
		cin>>K;
		cin>>(s+1);
		N=strlen(s+1);
		for(int i=1; i<=N; i++)a[i]=s[i]-'a'+1;
		a[N+1]=0;//后一位要清零,因为这个wa了好多遍 
		da(N,300);
		for(int i=1; i<=N; i++)dp[i][0]=Height[i];
		for(int j=1; (1<<j)<=N; j++)
		{
			for(int i=1; i+(1<<j)-1<=N; i++)
			{
				dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
			}
		}
		LL ans=0;
		for(int i=1,j; i<=N; i++)
		{
			j=i+K-1;
			if(j<=N)ans+=lcp(i,j);
			if(i-1>=1&&j<=N)ans-=lcp(i-1,j);
			if(j+1<=N)ans-=lcp(i,j+1);
			if(i-1>=1&&j+1<=N)ans+=lcp(i-1,j+1);
		}
		cout<<ans<<endl;
	}
}
 
2018焦作网络赛 H-String and Times
题目链接:https://nanti.jisuanke.com/t/31717
 我的后缀数组的模板竟然有问题。。。
 倍增那个循环
for(int len=1; len<n; len<<=1)
 
必选要改成这样才不会超时
for(int len=1; Rank[SA[n]]<n; len<<=1)
 
我也不知道为什么,感觉就我那样写也没有问题呀,没有陷入死循环的机会呀T_T
然后这道题就跟上面hdu 6194 差不多了,只不过这次求的刚好多少次重复是个范围了
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
const int MOD=1e9+7;
int SA[maxn],Rank[maxn];
int C1[maxn],C2[maxn],A[maxn],B[maxn],tsa[maxn];//基数排序的数组,以及辅助数组
int Height[maxn];//最重要的Height数组,Height[i]表示排名第i的后缀和排名第i-1的后缀的lcp
int a[maxn];//把字符串转成数字存下来
void da(int n,int m)//n是字符串长度,m是字符串的范围 
{
	for(int i=0; i<=m; i++)C1[i]=0;
	for(int i=1; i<=n; i++)C1[a[i]]++;
	for(int i=2; i<=m; i++)C1[i]+=C1[i-1];
	for(int i=n; i>=1; i--)SA[C1[a[i]]--]=i;
	Rank[SA[1]]=1;
	for(int i=2; i<=n; i++)
	{
		Rank[SA[i]]=Rank[SA[i-1]];
		if(a[SA[i]]!=a[SA[i-1]])Rank[SA[i]]++;
	}
	for(int len=1; Rank[SA[n]]<n; len<<=1)
	{
		for(int i=0; i<=n; i++)C1[i]=C2[i]=0;//这里一定是要弄到n 
		for(int i=1; i<=n; i++)
		{
			A[i]=Rank[i];
			if(i+len<=n)B[i]=Rank[i+len];
			else B[i]=0;
			C1[A[i]]++;
			C2[B[i]]++;
		}
		for(int i=1; i<=n; i++)C1[i]+=C1[i-1],C2[i]+=C2[i-1];
		for(int i=n; i>=1; i--)tsa[C2[B[i]]--]=i;
		for(int i=n; i>=1; i--)SA[C1[A[tsa[i]]]--]=tsa[i];
		Rank[SA[1]]=1;
		for(int i=2; i<=n; i++)
		{
			Rank[SA[i]]=Rank[SA[i-1]];
			if(A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]])Rank[SA[i]]++;
		}
	}
	for(int i=1,h=0; i<=n; i++) //求Height数组
	{
		if(h)h--;
		while(a[i+h]==a[SA[Rank[i]-1]+h])h++;
		Height[Rank[i]]=h;
	}
}
int N;
char s[maxn];
int dp[maxn][30];
int lcp(int L,int R)
{
	if(L==R)return N-SA[L]+1;
	L++;//加1是因为height[i]包含了i-1的值了 
	int k=0;
	while((1<<(k+1))<R-L+1)k++;
	return min(dp[L][k],dp[R-(1<<k)+1][k]);
}
int main()
{
	int cnt1,cnt2;
	while(scanf("%s%d%d",s+1,&cnt1,&cnt2)!=EOF)
	{
		N=strlen(s+1);
		for(int i=1; i<=N; i++)a[i]=s[i]-'A'+1;
		a[N+1]=0;
		da(N,27);
		for(int i=1; i<=N; i++)dp[i][0]=Height[i];
		for(int j=1; (1<<j)<=N+1; j++)
		{
			for(int i=1; i+(1<<j)-1<=N+1; i++)
			{
				dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
			}
		}
		LL ans=0;
		for(int i=1,j,k; i+cnt1-1<=N; i++)
		{
			j=i+cnt1-1;
			k=i+cnt2-1;
			if(j<=N)ans+=lcp(i,j);
			if(i-1>=1&&j<=N)ans-=lcp(i-1,j);
			if(k+1<=N)ans-=lcp(i,k+1);
			if(i-1>=1&&k+1<=N)ans+=lcp(i-1,k+1);
		}
		printf("%lld\n",ans);
	}
}
                
                  
                  
                  
                  
                            
      
          
                
                
                
                
              
                
                
                
                
                
              
                
                
              
            
                  
					2972
					
被折叠的  条评论
		 为什么被折叠?
		 
		 
		
    
  
    
  
            


            