字符串(AC自动机):HDU 5129 Yong Zheng's Death
Yong Zheng's Death
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 512000/512000 K (Java/Others)
Total Submission(s): 224 Accepted Submission(s): 37
historians like to do researches about this. For example, the 5th Qing
dynasty emperor Yong Zheng's death is often argued among historians.
Someone say that he was killed by the top assassin Lu Siniang whose
family were almost wiped out by Yong Zheng. Someone say that Yong Zheng
was poisoned to death because he liked to eat all kinds of Chinese
medicines which were said to have the functions of prolonging human
lives. Recently, a new document was discovered in Gu Gong(the Forbidden
City). It is a secret document written by Yong Zheng's most trusted
eunuch. It reads on the cover: "This document tells how Yong Zheng died.
But I can't write this in plain text. Whether people will understand
this is depend on Gods." Historians finally found out that there are
totally n strings in the document making a set S = { s1,s2,... sn}. You
can make some death ciphers from the set. A string is called a DEATH
CIPHER if and only if it can be divide into two substrings u and v, and u
and v are both a prefix of a string from S (Note that u and v can't be
empty strings, and u and v can be prefixes of a same string or two
different strings).
When all DEATH CIPHERs are put together, a readable article revealing Yong Zheng's death will appear.
Please help historians to figure out the number of different death ciphers.
For each case, the first line contains an integer n(1 <= n <= 10000), indicating the number of strings in S.
The following n lines contain n strings, indicating the strings in S.
Every string only consists of lower case letters and its length is
between 1 and 30(inclusive).
The input ends by n = 0.
For the sample , the death ciphers are {aa, aba, aca, aab, aac, abac, acab, abab, acac}
神题啊!在此我会介绍两种方法,都是构造法,构造是很跳脱的,这也是此题难点,写题解时有些小激动。
考虑答案的构成,会是两个前缀,我们叫它们A,B;网上很多WA的代码,就是建trie后输出cnt²,当然是错的,考虑当前枚举的A+B答案串,可能有多断点可以使得断点左边是A,右边是B,比如数据:3 abc bc c,这里abc答案串会被枚举两次,所以错误的原因是算重复了。
先建一个AC自动机。
两种解决思路:
第一种是补集转换,用总的-不合法的,总的就是cnt²,不合法的我们来分析,下图是某个答案串的情形。
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgsAAABtCAIAAACgD7X3AAADnUlEQVR4nO3bPW5aQRiFYS+EktVQeS0UrMWSl2J5KWYHFBYFQrKUFMgxRsdK8Ixz73w8j2jSRFdHzH35890vAEjupr4AAGZKIQDIFAKATCEAyBQCgEwhAMgUAoBMIQDIFAKATCEAyBQCgEwhAMgUAoBMIQDIFAKATCEAyBQCgEwhAMgUAoBMIQDIFAKATCEAyBQCgEwhAMgUAoBMIQDIFAKATCEAyBQCuMrzfrHcvT/2T1NfDj9JIWbq7eF+t1i+PmynvpA6juuP+5ptv+f0tDyvwnF9f3iZ7oL4YQoxT9vDarlfb3arx7epL6WM43r5sefL46tIXOtps1vowW1RiFl62uwWm+PL46t38f18KsTpn+vnKS9oMNvDSlNvjkLM0fvNa3tYuYt187kQtr2S1ys3SSHm5+wovj3ce1/fy6dCnN6lTXtBY3l5fPVUvD0KMTtvD/cfNy8fl/fjm+omCnGTFGJunveL808/tofV0qvdLsKnTH4IcIXnvazeHoWYmafN+etcvzrv6OKbar/MudZx7cXKzVGIWYkvbL3a7UMhmp3e0Z6Ptj2sbFiZQszJVz8XcS/rwadMXZz+aO794WlZnELMyNfv4i++nOA7Lr6ptif8lUIAkCkEAJlCAJApBACZQgCQKQQAmUIAkCkEAJlCAJApBACZQgCQKQQAmUIAkCkEAJlCAJApBACZQgCQKQQAmUIAkCkEAJlCAJB9pxB3AIxJIarZLZanx9QXAgxPIUr5kweRANopRCkK0YsNW1ivDIUoRSG6MGML61Xyvwvxjf+Bf3dxOKe+nFGZsYX1hqYQlTmcXZixhfWGphCVOZxdmLGF9YamEJU5nF2YsYX1hqYQlTmcXZixhfWGphCVOZxdmLGF9YamEJU5nF2YsYX1hqYQlTmcXZixhfWGphCVOZxdmLGF9YamEJU5nF2YsYX1hqYQlTmcXZixhfWGphCVOZxdmLGF9YamEJU5nF2YsYX1hqYQlTmcXZixhfWGphCVOZxdmLGF9YamEJU5nF2YsYX1hqYQlTmcXZixhfWGNnEh+FEXh3PqyxmVGVtYrxKFKMXh7MKMLaxXiUKU4nB2YcYW1qtEIUpxOLswYwvrVaIQ1TiZ7dzjWlivkh8vBIzIT3FaWO82KQQAmUIAkCkEAJlCAJApBACZQgCQKQQAmUIAkCkEAJlCAJApBACZQgCQKQQAmUIAkCkEAJlCAJApBACZQgCQKQQA2W872zlpaVUPdQAAAABJRU5ErkJggg==" alt="" />
这里已经假设A,B,C三个断点都是可以的,这表示[1~A],[1~B],[1~C],[A+1~len],[B+1~len],[C+1~len]都是前缀,总的方案数是3,不合法的方案数是2,现在来构造方法(我有三种方法):
1.求中间没有红线的两头都是红线的段数(A~B,B~C)
2.求左端点是最左边的红线的段数
3.求右端点是最右边的红线的段数
我们采用第一种方法,枚举前缀B,假设与某个A构成的A+B答案串中有当前B位置后面的断点,fail[B]处一定是最近的那个。
aaarticlea/png;base64,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" alt="" />
p1,p2为合法分割,fail[x]=p2,所以当前枚举的前缀B,我们可以接的A前缀只需要满足后缀是[p1~p2]即可。(这句话很重要)
代码很好理解:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int N=;
typedef long long LL;
int fa[N],len[N],deg[N];
int cnt,ch[N][],fail[N];
LL sum[N];queue<int>q;
char s[N];
struct ACM{
void Init(){
memset(fa,,sizeof(fa));
memset(sum,,sizeof(sum));
memset(deg,,sizeof(deg));
memset(fail,,sizeof(fail));
memset(ch,,sizeof(ch));cnt=;
}
void Insert(char *s){
int l=strlen(s);
for(int i=,p=;i<l;i++){
int c=s[i]-'a';
if(ch[p][c])p=ch[p][c];
else{
fa[++cnt]=p;
len[cnt]=len[p]+;
p=ch[p][c]=cnt;
}
}
}
void Build(){
for(int i=;i<;i++)
if(ch[][i])q.push(ch[][i]);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=;i<;i++)
if(ch[x][i]){
fail[ch[x][i]]=ch[fail[x]][i];
q.push(ch[x][i]);
}
else ch[x][i]=ch[fail[x]][i];
}
for(int i=;i<=cnt;i++)
sum[i]=;
for(int i=;i<=cnt;i++)
if(fail[i])deg[fail[i]]++;
for(int i=;i<=cnt;i++)
if(!deg[i])q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
sum[fail[x]]+=sum[x];
if(!--deg[fail[x]])
q.push(fail[x]);
}
}
LL Solve(){
LL ret=;
for(int i=;i<=cnt;i++){
int tmp=len[fail[i]],p=i;
while(tmp--)p=fa[p];
ret+=(fail[i]!=)*(sum[p]-);
}
return 1ll*cnt*cnt-ret;
}
}ac;
int n;
int main(){
while(true){
scanf("%d",&n);
if(!n)break;ac.Init();
for(int i=;i<=n;i++){
scanf("%s",s);
ac.Insert(s);
}
ac.Build();
printf("%lld\n",ac.Solve());
}
return ;
}
第二种方法是直接正着搞,DP出合法的即可(膜的poursoul大大的代码)
这也是构造,我先跑A串,使其尽量长,直到没有某个转移,不得不跳fail时开始DP,先上图。
有时候构造没有理由,不好理解不好发现,不过可以证明是对的。
aaarticlea/png;base64,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" alt="" />
现在于x计数,后面接的B串都是上图中第一二行的形式(上面应有省略号),我用我的感受引入一个乱yy的名词,"潜力",这里枚举出的最左断点是fail[x],fail[fail[x]],fail[fail[fail[x]]]……最开始时1~fail[x]那么长的一段就是ch[x][i](注意这里跳了fail)状态的"潜力",枚举B串,如果不能转移就跳fail,最后"潜力"没了,没了就不能跳fail了,即不能匹配了。
可以发现枚举的过程中"潜力"一直是尽可能的大,就是指断点在最左边。
dp[i][x]表示原x(和这里的x无关)位置后面匹配i个长度,在状态x的方案数,发现是可以转移的。
哎,讲不清讲不清,建议看代码,然后仔细地感受。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int N=,M=;
typedef long long LL;
int ch[N][],fail[N],fa[N],sln[N];
int n,cnt;LL ans,dp[M][N];
char s[N];queue<int>q; struct AC{
void Init(){
memset(fa,,sizeof(fa));
memset(ch,,sizeof(ch));
memset(dp,,sizeof(dp));
memset(sln,,sizeof(sln));
memset(fail,,sizeof(fail));
ans=cnt=;
}
void Insert(char *s){
int len=strlen(s);
for(int i=,p=;i<len;i++){
int c=s[i]-'a';
if(ch[p][c])p=ch[p][c];
else{
sln[++cnt]=sln[p]+;
fa[cnt]=p;p=ch[p][c]=cnt;
}
}
}
void Build(){
for(int i=;i<;i++)
if(ch[][i])
q.push(ch[][i]); while(!q.empty()){
int x=q.front();q.pop();
if(fail[x]!=)ans=ans+;
for(int i=;i<;i++)
if(ch[x][i]){
fail[ch[x][i]]=ch[fail[x]][i];
q.push(ch[x][i]);
}
else{
ch[x][i]=ch[fail[x]][i];
if(ch[x][i]){
dp[][ch[x][i]]+=;
ans+=;
}
}
}
}
void Solve(){
bool flag=;
for(int i=;flag;i++){
flag=;
for(int j=;j<=cnt;j++)if(dp[i-][j]){
for(int k=,t;k<;k++){
if(sln[t=ch[j][k]]<i)continue;
dp[i][t]+=dp[i-][j];flag=;
}
}
for(int j=;j<=cnt;j++)ans+=dp[i][j];
}
printf("%lld\n",ans);
}
}ac;
int main(){
while(~scanf("%d",&n)&&n){
ac.Init();
for(int i=;i<=n;i++){
scanf("%s",s);
ac.Insert(s);
}
ac.Build();ac.Solve();
}
return ;
}
字符串(AC自动机):HDU 5129 Yong Zheng's Death的更多相关文章
- HDU 5129 Yong Zheng's Death
题目链接:HDU-5129 题目大意为给一堆字符串,问由任意两个字符串的前缀子串(注意断句)能组成多少种不同的字符串. 思路是先用总方案数减去重复的方案数. 考虑对于一个字符串S,如图,假设S1,S2 ...
- 模板—字符串—AC自动机(多模式串,单文本串)
模板—字符串—AC自动机(多模式串,单文本串) Code: #include <queue> #include <cstdio> #include <cstring> ...
- 数据结构--AC自动机--hdu 2896
病毒侵袭 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submi ...
- 从0开始 数据结构 AC自动机 hdu 2222
参考博客 失配指针原理 使当前字符失配时跳转到另一段从root开始每一个字符都与当前已匹配字符段某一个后缀完全相同且长度最大的位置继续匹配,如同KMP算法一样,AC自动机在匹配时如果当前字符串匹配失败 ...
- [coci2012]覆盖字符串 AC自动机
给出一个长度为N的小写字母串,现在Mirko有M个若干长度为Li字符串.现在Mirko要用这M个字符串去覆盖给出的那个字符串的.覆盖时,必须保证:1.Mirko的字符串不能拆开,旋转:2.Mirko的 ...
- 字符串——AC自动机
目录 一.前言 二.思路 三.代码 四.参考资料 一.前言 以前一直没学AC自动机,主要是被名字吓到了,自动AC,这么强的名字肯定很难,学了后才发现,其实不难. AC自动机并不是Acept autom ...
- AC自动机 HDU 3065
大概就是裸的AC自动机了 #include<stdio.h> #include<algorithm> #include<string.h> #include< ...
- AC自动机 HDU 2896
n个字串 m个母串 字串在母串中出现几次 #include<stdio.h> #include<algorithm> #include<string.h> #inc ...
- 【bzoj1030】: [JSOI2007]文本生成器 字符串-AC自动机-DP
[bzoj1030]: [JSOI2007]文本生成器 首先把匹配任意一个的个数的问题转化为总个数-没有一个匹配的个数 先构造AC自动机,然后枚举每一位的字母以及在自动机上的位置 f[i][j]为第i ...
随机推荐
- java- 枚举的常见用法
用法一:常量 public enum MyColor{Red,Black,Blue} public enum Color { RED, GREEN, BLANK, YELLOW } enum为枚举类的 ...
- C# Unix时间戳转换为时间
在做一些接口的时候,比如返回数据中有一个时间的属性,它的值是使用Unix时间戳表示的,当我们处理它(保存到本地或者格式化前台展示)时需要转换成日期时间,在此就需要根据时间戳转换为日期时间 (注:Uni ...
- Dao模型设计(基于Dao与Hebernate框架)
以前没有Dao设计模型之前,一般都是这样的流程: ①先设计实体对象 学生对象: package com.itheima.domain; import java.io.Serializable; imp ...
- bzoj1231: [Usaco2008 Nov]mixup2 混乱的奶牛
思路:状压dp,设f[i][j]表示当前已经选出的牛的状态为i,最后一头选出的牛为j的方案数. 然后注意就是初值不能是f[0][i]=1,因为所有牛本来都可以第一个被选中,然而这样一定初值有些牛可能就 ...
- Web Service 的服务端的引用
1.先说说服务端的引用 先写一个Web Service 的文件 上图 创建一个web 项目或者网站 然后添加新项 创建一个web服务 得到 下面的页面 然后运行起来 然后复制下地址 接下来创建另一 ...
- 青瓷qici - H5小游戏 抽奖机 4 运行脚本编写
hello,小伙伴们,我们来继续编写相关的程序. 前几章我们已经基本把界面等问题搞定了,现在我们就来写脚本让整个流程统一起来. 看看我们现在有了什么?一个界面还有他的层次结构 青瓷界面绑定UI.js创 ...
- 响应式页面字体用什么单位:rem
html:62.5%//10pxbody:1.4rem;//14px... <!doctype html> <html> <head> <title>a ...
- CakePHP采用model的save方法更新数据所需查询
采用model的save方法更新数据所需查询 1. 验证时候要确认是update 或者 create,以便使用对应规则 public $validate = array( 'field_name' = ...
- 犯这个错误的肯定不止我一个 关于File
File.Create(string filePath)这种用法所有人都知道,这两天用到的时候却发现一个问题. 需要先判断文件是否存在,如果不存在则创建文件,然后向该文件写入数据,后续定时Append ...
- [转]CentOS Yum 命令详解
总所周知,Redhat和Fedora的软件安装命令是rpm,但是用rpm安 装软件最大的麻烦就是需要手动寻找安装该软件所需要的一系列依赖关系,超级麻烦不说,要是软件不用了需要卸载的话由于卸载掉了某个依 ...