1. 程式人生 > 實用技巧 >C Count New String(SAM建立n個子串)

C Count New String(SAM建立n個子串)

題:https://ac.nowcoder.com/acm/contest/5669/C

題解:

分析:核心點1:當我們把原串第一次進行f函式後,第二次的f函式一定是對第一次經過f函式後的串進行取子串。

   核心點2:因為f函式的特性,這n個子串我們可以以10(字符集)*N的節點代價來建立字典樹,考慮題解的倆個位置i,j,我們只需要記錄插入 j 的last值,在插入時就插入到這個last值後面(tire樹後面)(要插入j-i個)。

        這裡只用插入j-i個的理由:按照核心點1的求法,我們可能要求插入(j-i)-1,插入(j-i)-2...長度允許的話,但大可不必,因為在統計不同子串的時候,最長的(插入(j-i)個)的個數已經全涵蓋了當前(插入(j-i)-1,插入(j-i)-2...)的所有子串。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
const int M=1000006;
int trans[M<<1][26],slink[M<<1],maxlen[M<<1];
int endpos[M<<1];
int last,now,root,len;

void init(){
    now=last=root=1
; } void extend(int c){ maxlen[++now]=maxlen[last]+1; int p=last,np=now; while(p&&!trans[p][c]){ trans[p][c]=np; p=slink[p]; } if(!p) slink[np]=root; else{ int q=trans[p][c]; if(maxlen[p]+1!=maxlen[q]){ maxlen[++now]=maxlen[p]+1
; int nq=now; memcpy(trans[nq],trans[q],sizeof(trans[q])); slink[nq]=slink[q]; slink[q]=slink[np]=nq; while(p&&trans[p][c]==q){ trans[p][c]=nq; p=slink[p]; } } else slink[np]=q; } last=np; endpos[np]=1; } int pos[20],lastpos[M]; char s[M]; int main(){ scanf("%s",s+1); init(); int len=strlen(s+1); reverse(s+1,s+1+len); lastpos[0]=1; for(int i=1;i<=len;i++){ last=lastpos[pos[s[i]-'a']]; for(int j=pos[s[i]-'a']+1;j<=i;j++){ extend(s[i]-'a'); lastpos[j]=last; } for(int j=0;j<=s[i]-'a';j++) pos[j]=i; } ll ans=0; for(int i=root+1;i<=now;i++) ans+=maxlen[i]-maxlen[slink[i]]; printf("%lld\n",ans); return 0; }
View Code

補充:sam的板子的last值是預設上一位的狀態值,因為模板只用對一個字串進行操作,而對多個串進行建sam時可以依據字符集的大小記錄最後插入的last,每次插入時可直接在記錄的last插入。