【Loj】 6071「2017 山东一轮集训 Day5」字符串

题目链接

点击打开链接

题目解法

题目的意思是有很多字符串,问依次把每个串的子串拼起来有多少种不同的字符串
子串问题可以想到用 S A M SAM SAM
考虑对于每个字符串建立单独的 S A M SAM SAM
现在需要考虑如何将串联系起来

考虑 2 2 2 种相同的拼法 “ a b c ” + “” “abc”+“” abc+“” “ a b ” + “ c ” “ab”+“c” ab+c,我们可以想到规定每个串必须选到最长在到下一个串,这样就不会重复
体现在 S A M SAM SAM 上就是再一个单独的 S A M SAM SAM 中走到头不能走了,再换到下一个 S A M SAM SAM 继续走
所以我们可以建立一些新指针,如 u u u 不能通过 c c c 继续走了,那么把 c h [ u ] [ c ] ch[u][c] ch[u][c] 变成 c h [ 下一个 S A M 的根 ] [ c ] ch[下一个SAM的根][c] ch[下一个SAM的根][c]

然后按照普通 S A M SAM SAM 统计子串的个数就可以了

#include <bits/stdc++.h>
using namespace std;
const int N(2000100),P(1e9+7);
struct Node{
	int len,fa,ch[26];
}node[N]; 
int n,m,root[N],dp[N],last,tot;
char str[N];
bool vis[N];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void dfs(int u,int rt2){
	if(vis[u]) return;
	vis[u]=1;
	for(int i=0;i<26;i++)
		if(node[u].ch[i]) dfs(node[u].ch[i],rt2);
		else vis[node[u].ch[i]=node[rt2].ch[i]]=1;
}
int calc(int u){
	if(dp[u]) return dp[u];
	dp[u]=1;
	for(int i=0;i<26;i++) if(node[u].ch[i]) (dp[u]+=calc(node[u].ch[i]))%=P;
	return dp[u];
}
void extend(int c,int rt){
	int p=last,np=last=++tot;node[np].len=node[p].len+1;
	for(;p&&!node[p].ch[c];p=node[p].fa) node[p].ch[c]=np;
	if(!p) node[np].fa=rt;
	else{
		int q=node[p].ch[c];
		if(node[q].len==node[p].len+1) node[np].fa=q;
		else{
			int nq=++tot;node[nq]=node[q],node[nq].len=node[p].len+1;
			node[np].fa=node[q].fa=nq;
			for(;p&&node[p].ch[c]==q;p=node[p].fa) node[p].ch[c]=nq;
		}
	}
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		scanf("%s",str+1);
		root[i]=last=++tot;
		int len=strlen(str+1);
		for(int j=1;j<=len;j++) extend(str[j]-'a',root[i]);
	}
	for(int i=n-1;i>=1;i--) dfs(root[i],root[i+1]);
	printf("%d",calc(root[1]));
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值