BZOJ 4502: 串 AC自动机

4502: 串

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 195  Solved: 95
[Submit][Status][Discuss]

Description

兔子们在玩字符串的游戏。首先,它们拿出了一个字符串集合S,然后它们定义一个字

符串为“好”的,当且仅当它可以被分成非空的两段,其中每一段都是字符串集合S中某个字符串的前缀。

比如对于字符串集合{"abc","bca"},字符串"abb","abab"是“好”的("abb"="ab"+"b",abab="ab"+"ab"),而字符串“bc”不是“好”的。

兔子们想知道,一共有多少不同的“好”的字符串。

Input

第一行一个整数n,表示字符串集合中字符串的个数

接下来每行一个字符串

Output

一个整数,表示有多少不同的“好”的字符串

Sample Input

2
ab
ac

Sample Output

9

HINT

1<=n<=10000,每个字符串非空且长度不超过30,均为小写字母组成。

Source

颂魔和毒爷把这道题加强了一下$\sum S \le 10^6$,然后给了一个更容易的做法。我偷一发题解.....

正解大概是讲:先钦定一个串C,只在最右边分割点统计。对于{S}中两前缀A,B。定义(A,B)合法仅当不存在划分B的一个前缀,接到A后面得到的(A‘,B‘)。那么就考虑一对(A,B)是否合法。

先枚举B,然后再统计多少A后面可以接B的前缀。这里是可以接,而不是接多少次,所以直接用最短的前缀其判断。

这个最短的另一个要求是B‘也存在{S}集中。所以可以等价于求一个最长的B‘。就是找一个最长的B的后缀,这个可以用fail树求出。

由B‘就定位B的最短前缀(Trie树定位),然后就统计它是{S}多少个A‘的后缀(用fail树统计)。

无声PPT

Code

#include< cstdio >
#include< cstring >

#define gec getchar
#define FILE(F) freopen(F".in","r",stdin),freopen(F".out","w",stdout)
#define DEBUG fprintf(stderr,"Passing [%s] in Line (%d)\n",__FUNCTION__,__LINE__)

typedef long long ll;
template
inline void read(T &x)
{
	x=0;bool f=0;char c=gec();
	for(;c<‘0‘||c>‘9‘;c=gec())f=(c==‘-‘);
	for(;c>=‘0‘&&c<=‘9‘;c=gec())x=x*10+c-‘0‘;
	x=f?-x:x;
}

const int MAXN(1000010);
int Case,n,leng; char str[MAXN];
ll Ans;

namespace ACmaton
{
	struct ACtrie
	{
		int nx[26],fail,sum,Dep;
	}trie[MAXN];int ktot=1,root=1;

	void ins()
	{
		int k=1;
		for(int i=1;i<=leng;i++)
		{
			if(!trie[k].nx[str[i]-‘a‘])trie[k].nx[str[i]-‘a‘]=++ktot;
			trie[trie[k].nx[str[i]-‘a‘]].Dep=trie[k].Dep+1;
			k=trie[k].nx[str[i]-‘a‘];
		}
	}

	int que[MAXN],l,h,now;
	void BFS()
	{
		for(int v=0;v<26;v++)
		if(trie[root].nx[v])
		{
			trie[trie[root].nx[v]].fail=root;
			que[++l]=trie[root].nx[v];
		}else trie[root].nx[v]=root;
		while(h<l)
		{
			now=que[++h];
			for(int v=0;v<26;v++)
			if(trie[now].nx[v])
			{
				trie[trie[now].nx[v]].fail=trie[trie[now].fail].nx[v];
				que[++l]=trie[now].nx[v];
			}else trie[now].nx[v]=trie[trie[now].fail].nx[v];
		}
	}

	int p[MAXN],cnt[MAXN];
	void Pretreat()
	{
		for(int i=1;i<=ktot;i++)cnt[trie[i].Dep]++;
		for(int i=1;i<=ktot;i++)cnt[i]+=cnt[i-1];
		for(int i=ktot;i>=1;i--)p[cnt[trie[i].Dep]--]=i;
		for(int i=ktot;i>=1;i--)
		{
			trie[p[i]].sum++;
			trie[trie[p[i]].fail].sum+=trie[p[i]].sum;
		}
		trie[root].sum=1;
	}

	int st[MAXN],tp;
	void Dfs(int x)
	{
		st[++tp]=x;
		for(int v=0;v<26;v++)
		if(trie[trie[x].nx[v]].Dep==trie[x].Dep+1)Dfs(trie[x].nx[v]);
		int Pre=trie[x].Dep-trie[trie[x].fail].Dep;
		tp--;if(trie[x].fail!=root)Ans-=trie[st[Pre+1]].sum-1;//保留本身一个
	}

}using namespace ACmaton;

int main()
{
	FILE("string");
	read(Case);
	read(n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",str+1);leng=strlen(str+1);
		ins();
	}
	Ans=((ll)ktot-1ll)*(ktot-1);
	BFS();
	Pretreat();
	Dfs(root);
	printf("%lld\n",Ans);
	return 0;
}
时间: 2024-12-19 06:52:55

BZOJ 4502: 串 AC自动机的相关文章

Bzoj1195 [HNOI2006]最短母串 [AC自动机]

Time Limit: 10 Sec  Memory Limit: 32 MBSubmit: 1304  Solved: 439 Description 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串. Input 第一行是一个正整数n(n<=12),表示给定的字符串的个数.以下的n行,每行有一个全由大写字母组成的字符串.每个字符串的长度不超过50. Output 只有一行,为找到的最短的字符串T.在保证最短的前提下,如果

[BZOJ 3530] [Sdoi2014] 数数 【AC自动机+DP】

题目链接:BZOJ - 3530 题目分析 明显是 AC自动机+DP,外加数位统计. WZY 神犇出的良心省选题,然而去年我太弱..比现在还要弱得多.. 其实现在做这道题,我自己也没想出完整解法.. 就想出了个 O(l^3) 的做法: 完全按照数位统计的思想来,先统计长度不足 len 的数字的合法种类数,这个枚举开头,然后 AC 自动机 DP 一下,用 f[i][j] 表示到了第 i 位,在第 j 个节点上的合法数字个数.这样是 O(L^2). 然后长度等于 n 的部分,就按照数位统计,一位位向

[BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】

题目链接:BZOJ - 1009 题目分析 题目要求求出不包含给定字符串的长度为 n 的字符串的数量. 既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j 位的字符串个数,然后转移就是可以从第 j 位加上一个字符转移到另一个位置. 然而..我并没有写过KMP + DP,我觉得还是写AC自动机+DP比较简单..于是,尽管只有一个模式串,我还是写了AC自动机+DP. 然后就是建出AC自动机,f[i][j] 表示长度为 i ,走到节点 j 的字符串的个数.

【暖*墟】 #AC自动机# 多模式串的匹配运用

一.构建步骤 1.将所有模式串构建成 Trie 树 2.对 Trie 上所有节点构建前缀指针(类似kmp中的next数组) 3.利用前缀指针对主串进行匹配 AC自动机关键点一:trie字典树的构建过程 字典树的构建过程是这样的,当要插入许多单词的时候,我们要从前往后遍历整个字符串, 当我们发现当前要插入的字符其节点再先前已经建成,我们直接去考虑下一个字符即可, 当我们发现当前要插入的字符没有再其前一个字符所形成的树下没有自己的节点, 我们就要创建一个新节点来表示这个字符,接下往下遍历其他的字符.

AC自动机:BZOJ 2434 阿狸的打字机

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1834  Solved: 1053[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽

hdu5384 AC自动机模板题,统计模式串在给定串中出现的个数

http://acm.hdu.edu.cn/showproblem.php?pid=5384 Problem Description Danganronpa is a video game franchise created and developed by Spike Chunsoft, the series' name is compounded from the Japanese words for "bullet" (dangan) and "refutation&q

BZOJ 1030 [JSOI2007]文本生成器(AC自动机)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1030 [题目大意] 求出包含任意一个给定串的串数量 [题解] 我们求出不包含任意一个给定串的数量,用全集去减即可, 对于给定串建立AC自动机,用1节点作为根,0节点向1连全字符集转移作为超级源, 那么0->match能匹配所有不包含给定串的串, 记dp[i][j]表示匹配了i长度,匹配到AC自动机j节点的串数量, 统计之后取补集即可. [代码] #include <cstdio&g

BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状数组维护, DFS到的查询点就回答询问.时间复杂度O(|ACAM|+QlogQ) ------------------------------------------------------------------------------------------- #include<cstdio>

AC自动机 - AC自动机 - 多模式串的匹配运用 --- HDU 3065

病毒侵袭持续中 Problem's Link:http://acm.hdu.edu.cn/showproblem.php?pid=3065 Mean: 中文题,不解释. analyse: AC自动机的运用.这一题需要将模式串都存储下来,还有就是base的取值一定要弄清楚,由于这题的模式串都是大写字母所以我们可以通过剪枝来加速. Time complexity:o(n)+o(ml)  Source code: // Memory Time // 1347K 0MS // by : Snarl_js