1. 程式人生 > 其它 >P3975 [TJOI2015]弦論(字尾自動機)

P3975 [TJOI2015]弦論(字尾自動機)

對於t=0的情況:

在不同位置出現的子串算同一個子串,這裡巨大的坑點是要把字尾自動機上除起點以外的所有點的sz置為1(不管它是虛擬節點還是真實節點),這一點萌新還沒理解...

然後把所有節點按照len值從小到大排序,然後做一個倒著的轉移,每個節點首先能轉移出sz個子串,然後加上它後繼節點的貢獻,可以求出每個節點往後可以轉移出多少種子串。

然後貪心的找。

對於t=1的情況,和以前一樣,求出每個節點所代表的字串集合在母串中出現了幾次(即子樹大小)。

然後把所有節點按照len值從小到大排序,倒著轉移。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+100;
int len[maxn],link[maxn],nxt[maxn][26];
int sz[maxn];
int tot=1,lst=1;
string s;
int n,t,k;
void sam_extend (char c) {
	int cur=++tot;
	len[cur]=len[lst]+1;
	sz[cur]=1;
	int p=lst;
	while (p&&!nxt[p][c-'a']) {
		nxt[p][c-'a']=cur;
		p=link[p];
	}
	if (!p) {
		link[cur]=1;
	}
	else {
		int q=nxt[p][c-'a'];
		if (len[p]+1==len[q]) {
			link[cur]=q; 
		}
		else {
			int clone=++tot;
			len[clone]=len[p]+1;
			for (int i=0;i<26;i++) {
				nxt[clone][i]=nxt[q][i];
			}
			link[clone]=link[q];
			while (p&&nxt[p][c-'a']==q) {
				nxt[p][c-'a']=clone;
				p=link[p];
			} 
			link[q]=link[cur]=clone;
		}
	}
	lst=cur;
	sz[cur]=1;
}
vector<int> g[maxn];
void dfs (int u) {
	for (int v:g[u]) {
		dfs(v);
		sz[u]+=sz[v];
	}
} 
int a[maxn];
int sum[maxn];
int cmp (int x,int y) {
	return len[x]<len[y];
}
int main () {
	cin>>s>>t>>k;
	for (char i:s) sam_extend(i);
	for (int i=2;i<=tot;i++) g[link[i]].push_back(i);
	if (!t)for (int i=2;i<=tot;i++) sz[i]=1;
	if (t) dfs(1);
	for (int i=1;i<=tot;i++) a[i]=i;
	sort(a+1,a+tot+1,cmp);
	if (t) sz[1]=0;
	for (int i=tot;i>=1;i--) {
		sum[a[i]]=sz[a[i]];
		for (int j=0;j<26;j++) {
			if (nxt[a[i]][j]) {
				sum[a[i]]+=sum[nxt[a[i]][j]];
				//sum[i]表示節點i往後可以轉移出多少條不同的子串 
			}
		}
	}
	if (k>sum[1]) return printf("-1\n"),0;
	int u=1;
	while (k>0) {
		int p=0;
		while (k>sum[nxt[u][p]]) {
			k-=sum[nxt[u][p]];
			p++;
		}
		u=nxt[u][p];
		printf("%c",p+'a');
		k-=sz[u];
	}
}