BZOJ5084 hashit

hashit

你有一个字符串S,一开始为空串,要求支持两种操作:

  • 在S后面加入字母C。
  • 删除S最后一个字母。

问每次操作后S有多少个两两不同的连续子串。

|Q|<=105

题解

https://blog.csdn.net/qq_36797743/article/details/78841291

很明显,题目给你的是一个字典树。你可以dfs下去,顺便建立SAM。

然后在回溯的时候将SAM还原,就可以O(n)(?)地解决这个问题了

我思忖后缀自动机怎么个还原法。原来是直接记录修改操作……学习了

但是这个谁都看得出来复杂度不对……

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100005;
char str[N];
int len;
int now,tot,last;
struct trie {
	int ch[26],fa,c;//他自己是什么字符
} o[N];
void build_tree () {
	now=0,tot=0;
	for (int u=0; u<len; u++) {
		if (str[u]=='-') now=o[now].fa;
		else {
			int x=str[u]-'a';
			if (o[now].ch[x]==0) {
				o[now].ch[x]=++tot,o[tot].c=x,o[tot].fa=now;
			}
			now=o[now].ch[x];
		}
	}
}
struct sam {
	int ch[26],len,fa;
} s[N<<2];
int res=0;//现在有多少个不同的串了
struct operation {
	int x,y,z;
	int op;//什么类型
} sta[N<<2]; //撤销操作
int num=0;//当前撤销到哪里
void extend (int x) { //插入这个新的点
	int p=last,np=++tot;
	s[np].len=s[p].len+1;
	while (p!=0&&s[p].ch[x]==0) {
		/**/
		num++;
		sta[num].x=p,sta[num].y=x,sta[num].z=s[p].ch[x],sta[num].op=0;
		/**/
		s[p].ch[x]=np,p=s[p].fa;
	}
	if (p==0)   s[np].fa=1;
	else {
		int q=s[p].ch[x];
		if (s[p].len+1==s[q].len) s[np].fa=q;
		else {
			int nq=++tot;
			s[nq]=s[q],s[nq].len=s[p].len+1;
			/**/
			num++;
			sta[num].x=q,sta[num].y=s[q].fa,sta[num].op=1;
			/**/
			s[q].fa=s[np].fa=nq;
			while (p!=0&&s[p].ch[x]==q) {
				/**/
				num++,sta[num].x=p,sta[num].y=x,sta[num].z=s[p].ch[x],sta[num].op=0;
				/**/
				s[p].ch[x]=nq,p=s[p].fa;
			}
		}
	}
	res=res+s[np].len-s[s[np].fa].len;
	last=np;
}
int ans[N];//每个节点的答案
void dfs (int x) {
	int old_num=num,old_res=res,ooo=last;
	extend(o[x].c);
	ans[x]=res;
	for (int u=0; u<26; u++)
		if (o[x].ch[u]!=0)
			dfs(o[x].ch[u]);
	for (int u=old_num+1; u<=num; u++) { //撤销操作
		int x=sta[u].x,y=sta[u].y,z=sta[u].z;
		if (sta[u].op==0)//ch的修改
			s[x].ch[y]=z;
		if (sta[u].op==1)//fa的修改
			s[x].fa=y;
	}
	res=old_res,last=old_last,num=old_num;
}
void solve () {
	tot=1;
	for (int u=0; u<26; u++)
		if (o[0].ch[u]!=0) {
			s[1].len=0,s[1].fa=0;
			for (int i=0; i<26; i++) s[1].ch[i]=0;
			last=1,res=0;
			dfs(o[0].ch[u]);
		}
	now=0;
	for (int u=0; u<len; u++) {
		if (str[u]=='-') now=o[now].fa;
		else {
			int x=str[u]-'a';
			now=o[now].ch[x];
		}
		printf("%d\n",ans[now]);
	}
}
int main() {
	scanf("%s",str),len=strlen(str);
	build_tree(),solve();
	return 0;
}

posted on 2019-05-22 12:48  autoint  阅读(134)  评论(0)    收藏  举报

导航