HDU 4622/4641 後綴自動機簡單應用

4641

4641因爲是多組數據所以爲了方便把板子改成了用數組的二而不是指針的寫法。

計算出現k次以上的字符串一共有幾個。

對於已經建立好的SAM,每加入一個字符,多出來了一次的字符串就是所有的以這個字符爲結尾的後綴字符串。

所以只需要統計加入這個字符之後又多了幾個就可以了,用後綴數組就可以實現。

記錄一下每個狀態的right集合的大小(size的求法可以想象成一個樹形dp)

然後如果某種狀態的right集合的大小達到了K,那麼就將答案加上val[p]-val[fa[p]]

#include<bits/stdc++.h>
#include<iostream>
#include<string.h>
#define N 500005 
#define INF 0x3f3f3f3f
using namespace std;
char s[50005];
char s1[10];

int n,m,k,tot=0;
long long ans=0;
struct state{
    state *par, *go[26];
    int val,size;  //val==maxlen  fst==very left
    state(int _val) :
        par(0), val(0), size(0){
        memset(go,0,sizeof(go));
    }
};

struct suffixautomaton{
    int fa[N],go[N][26];
    int val[N], size[N];
    int root,last;
    int id;
    void extend(int w){
        int p=last;
        int np=++id;
        val[np]=val[p]+1;
        while (p!=-1&&go[p][w]==0){
            go[p][w]=np;
            p=fa[p];
        }
        if (p==-1) fa[np]=root;
        else{
            int q=go[p][w];
            if (val[p]+1==val[q]){
                fa[np]=q;
            }
            else{
                int nq=++id;
                val[nq]=val[p]+1;
                memcpy(go[nq],go[q],sizeof(go[q]));
                size[nq]=size[q];
                fa[nq]=fa[q];
                fa[q]=fa[np]=nq;
                while (p!=-1&&go[p][w]==q) {
                    go[p][w]=nq;
                    p=fa[p];
                }
            }
        }
        last=np;
        while (np&&size[np]<k){
            size[np]++;
            if (size[np]>=k) ans+=(val[np]-val[fa[np]]);
            np=fa[np];
        }
    }
    
    void init(char *s){
        memset(go,0,sizeof(go));
        memset(val,0,sizeof(val));
        memset(size,0,sizeof(size));
        memset(fa,0,sizeof(fa));
        
        root=last=id=0;
        fa[root]=-1;
        int len=strlen(s+1);
        for (int i=1;i<=len;i++){
            extend(s[i]-'a');
        }
    }
}A;

int main(){
while (scanf("%d%d%d",&n,&m,&k)!=EOF){
    ans=0; 
    scanf("%s",s+1);
    A.init(s);
    int typ;
    while (m--){
        scanf("%d",&typ);
        if (typ==1){
            scanf("%s",s1);
            A.extend(s1[0]-'a');
        }
        else{
            printf("%lld\n",ans);
        }
    }
}
return 0;
} 

4622

這個題目剛開始使用memset超時……佛了

後來又因爲調試代碼沒有刪除WA了幾次……

題目是後綴數組的板子題目?

求某個子串中相互不相同的子串的個數,由於字符串的大小隻有2000,所以就可以直接預處理好所有的情況然後回答。

預處理的方法就是裸的SAM加上一句話……

加上這個字符之後如果出現了一個新的字符串,這個字符串一定是長度在(val[fa[p]], val[p]]之間的。

#include<bits/stdc++.h>
#include<iostream>
#include<string.h>
#define N 100005
#define LEN 2005

using namespace std;
char s[LEN];
int n,m,k,tot=0;
int ans[LEN][LEN];

struct suffixautomaton{
	int fa[N],go[N][26];
	int val[N];
	int root,last;
	int id,	sum;
	int newnode(int v){
		id++;
		for (int i=0;i<26;i++) go[id][i]=0;
		val[id]=v;
		return id;
	}
	void extend(int w){
		int p=last;
		int np=newnode(val[p]+1);
		while (p!=-1&&go[p][w]==0){
			go[p][w]=np;
			p=fa[p];
		}
		if (p==-1) fa[np]=root;
		else{
			int q=go[p][w];
			if (val[p]+1==val[q]){
				fa[np]=q;
			}
			else{
				int nq=newnode(val[p]+1);
				memcpy(go[nq],go[q],sizeof(go[q]));
				fa[nq]=fa[q];
				fa[q]=fa[np]=nq;
				while (p!=-1&&go[p][w]==q) {
					go[p][w]=nq;
					p=fa[p];
				}
			}
		}
		last=np;
		sum+=(val[np]-val[fa[np]]);
		return;
	}
	
	void init(char *s, int st, int len){
		id=-1;
		root=last=newnode(0);
		fa[root]=-1;
		sum=0;
		for (int i=st;i<=len;i++){
			extend(s[i]-'a');
			ans[st][i]=sum; 
		}
	}
}A;

int main(){
	int T;
	scanf("%d",&T);
	while (T--){
		scanf("%s",s+1);
		int len=strlen(s+1);
		for (int i=1;i<=len;i++){
			A.init(s,i,len);
		}/*
		for (int i=1;i<=len;i++){
			for (int j=i;j<=len;j++){
				cout<<ans[i][j]<<" ";
			}
			cout<<endl;
		}*/
		int m,l,r;
		scanf("%d",&m);
		while (m--){
			scanf("%d%d",&l,&r);
			printf("%d\n",ans[l][r]);
		}
	} 
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章