1. 程式人生 > >Newcoder 139 I.Substring(字尾陣列+組合數學)

Newcoder 139 I.Substring(字尾陣列+組合數學)

Description

兩個字串u1...ukv1...vl是同構的當且僅當k=l且存在一個單射g使得ui=g(vi),1ik. 給出一字串s1...sn,該字串的n(n+1)2個子串中互不同構的子串數量

Input

多組用例,每組用例首先輸入一整數n表示字串長度,之後輸入一個長度為n的字串s

(1n5104,si{a.b.c}),n2105

Output

輸出s子串中互不同構的子串數量

Sample Input

4 abaa 4 abab

Sample Output

6 4

Solution

對映只有六種,一個子串有超過一種字元則通過這六種對映可以變成六個同構但不一樣的串,而相同字元組成的串通過六種對映只會變成三個同構但不一樣的串,將該字串通過六種對映後的六個串連在一起(用不同字元隔開),用字尾陣列求出所有不含所加額外字元的不同子串數量num(即6n(n+1)2heighti),同時統計連續相同字元出現次數最大值mx,那麼這mx個由相同字元組成的串會被計算3次而不是6次,繼而答案為num+3mx6

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 300010
typedef long long ll;
int t1[maxn],t2[maxn],c[maxn],sa[maxn],Rank[maxn],height[maxn];
bool cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int str[],int
n,int m) { n++; int i,j,p,*x=t1,*y=t2; for(i=0;i<m;i++)c[i]=0; for(i=0;i<n;i++)c[x[i]=str[i]]++; for(i=1;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i; for(j=1;j<=n;j<<=1) { p=0; for(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++)c[i]=0; for(i=0;i<n;i++)c[x[y[i]]]++; for(i=1;i<m;i++)c[i]+=c[i-1]; for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1;x[sa[0]]=0; for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++; if(p>=n)break; m=p; } int k=0; n--; 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(str[i+k]==str[j+k])k++; height[Rank[i]]=k; } } int n,a[maxn]; char s[maxn]; int main() { while(~scanf("%d",&n)) { scanf("%s",s); int b[3]; for(int i=0;i<3;i++)b[i]=i; int temp=4; int res=0; do { for(int i=0;i<n;i++)a[res++]=b[s[i]-'a']+1; a[res++]=temp++; }while(next_permutation(b,b+3)); a[--res]=0; da(a,res,9); ll ans=3ll*n*(n+1); for(int i=2;i<=res;i++)ans-=height[i]; int mx=1,len=1; for(int i=1;i<=n;i++) { if(i==1||s[i]!=s[i-1])mx=max(mx,len),len=1; else len++; } ans=(ans+3*mx)/6; printf("%lld\n",ans); } return 0; }