1. 程式人生 > >牛客網暑期ACM多校訓練營(第一場)

牛客網暑期ACM多校訓練營(第一場)

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多校訓練營(第一場)