牛客網暑期ACM多校訓練營(第一場)
阿新 • • 發佈:2018-07-21
mes cab uri 字符 class ast %s 會有 子串
solve 2 (D J)
rank 138/744 18.55%
J題 做法:復制一份數組,拼接到原數組後面,然後求一段區間不同數字的個數。樹狀數組優化。
D題 做法:暴力枚舉,然後用set去重。
I題 補題:
題意:給你一個長度為n的字符串,問這個字符串有多少種不同構的子串。
比如abc和acb,bac,bca,cab,cba同構。
思路:既然要求不同構的子串個數,我們不妨把6種同構字符串拼接在一起,建立SAM,對於每種串,都連接在root上,統計不同的子串個數。
那麽,若子串裏含有2種及以上的字符的時候,它有6種同構字符串,且一定都在SAM中存在,且被統計過。
所以,這種字符串每6個,價值為1。(其余5種都是同構的,所以不計數)
還有一種串,只由一種字符串組成,比如aaa,bbb,ccc,它們互相同構,這種只會有3種同構字符串。
怎麽計算個數呢?很簡單,只需要找到在原串中,最長連續單一子串的長度是多少便可,對於所有的單一串,這一個串包含它們所有。
所以答案就出來了。
貼代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+10;
char s[maxn];
int n;
int pa[maxn<<2],son[maxn<<2][27];
int deep[maxn<<2 ],cnt,root,last;
ll tot;
inline int newnode(int _deep){
deep[++cnt]=_deep;
return cnt;
}
inline void sam(int alp){
int np=newnode(deep[last]+1);
int u=last;
memset(son[np],0,sizeof son[np]);
while(u && !son[u][alp])son[u][alp]=np,u=pa[u];
if(!u)pa[np]=root;
else{
int v=son[u][alp];
if(deep[v]==deep[u]+1)pa[np]=v;
else{
int nv=newnode(deep[u]+1);
memcpy(son[nv],son[v],sizeof son[v]);
pa[nv]=pa[v],pa[v]=pa[np]=nv;
while(u&&son[u][alp]==v)son[u][alp]=nv,u=pa[u];
}
}
last=np;
tot+=deep[np]-deep[pa[np]];
}
inline void pre(){
cnt=0;
memset(son[1],0,sizeof son[1]);
root=last=newnode(0);
tot=0;
}
int main(){
while(scanf("%d",&n)!=EOF){
scanf("%s",s);
pre();
for(int i=0;i<n;i++){
if(s[i]==‘a‘){
s[i+n]=‘a‘;s[i+2*n]=‘b‘;s[i+3*n]=‘b‘;s[i+4*n]=‘c‘;s[i+5*n]=‘c‘;
}else if(s[i]==‘b‘){
s[i+n]=‘c‘;s[i+2*n]=‘a‘;s[i+3*n]=‘c‘;s[i+4*n]=‘a‘;s[i+5*n]=‘b‘;
}else{
s[i+n]=‘b‘;s[i+2*n]=‘c‘;s[i+3*n]=‘a‘;s[i+4*n]=‘b‘;s[i+5*n]=‘a‘;
}
}/**得到6種同構串,拼接**/
n+=5*n;
int ct=0;
for(int i=0;i<n;i++){
sam(s[i]-‘a‘);ct++;
if(ct==n/6){
ct=0;
last=1;
}
}
ll tot2=1;
ll cnt2=1;
for(int i=1;i<n/6;i++){
if(s[i]==s[i-1]){
cnt2++;
tot2=max(tot2,cnt2);
}else{
cnt2=1;
}
}
//cout<<tot<<" "<<tot2<<endl;
printf("%lld\n",(tot+3*tot2)/6);
}
return 0;
}
牛客網暑期ACM多校訓練營(第一場)