HDU 6096 (String) AC自动机
来源:互联网 发布:怎么找淘宝客服工作 编辑:程序博客网 时间:2024/05/22 02:23
题目大意
现在有N个单词,Q次询问,每次询问包含两个字符串a和b,求出以a为前缀且以b为后缀的单词共有多少个,其中a和b不互相覆盖。
解题思路
题目中所说的a和b不互相覆盖的含义是当 a =“ac”b = “cm”时,“acm”串不满足条件,因为其以a为前缀且以b为后缀时,a和b相互覆盖了。
题解中所说的做法没太看懂,现在给出一种巧妙构造并利用AC自动机解题的方法。
由于询问的时候,每次给出两个字符串并不便于操作,不妨将后缀与前缀拼接在一起,并使用特殊的符号进行标记。将所有询问离线,建立AC自动机。并将给出的N个单词拼在一起,具体操作为:
1.将每个单词复制一份拼接在原单词后面,中间使用一种分隔符连接,此分隔符与询问中的区分前后缀的分隔符相同,构成新的单词。
2.将新的单词依次连接起来,中间使用另一种分隔符连接,将所有单词连接成一篇文章。
3.原问题转化为,求出一篇文章中的存在的每种模式串的数量,是经典的AC自动机求解问题。
为了方便理解,此处以样例作为栗子
Sample Input
**1
4 4
aba
cde
acdefa
cdef
a a
cd ef
ac a
ce f**
将给出的单词拼接成文章为:
aba|aba#cde|cde#acdefa|acdefa#cdef|cdef#
将前后缀处理为新的字符串:
S1=“a|a”,S2=”ef|cd”,S3=“a|ac”,S4 = “f|ce”
将S1-S4依次insert,建成AC自动机,离线询问
扫描文章,对于每次询问依次回答即可
小Tips:由于题目中给出了前后缀不能覆盖的条件,所以扫描文章的时候需要加入特判。
附上代码
#include <cstdio>#include <cstring>#include <algorithm>#include <queue>#define maxnode 2005000#define sigma 30using namespace std;typedef long long ll;int cnt=1;char que[2550000],in[2000000],head[2000000],tail[2000000],key[2000000];int ask[1001000],res[1010000];struct ac_automation{ int ch[maxnode][sigma]; int val[maxnode]; int last[maxnode]; int f[maxnode]; int num[maxnode]; int dep[maxnode]; int sz,ans; void clear() { sz=1;ans=0; memset(ch[0],0,sizeof(ch[0])); memset(last,0,sizeof(last)); memset(val,0,sizeof(val)); memset(num,0,sizeof(num)); } int idx(char sign) { if(sign>='a'&&sign<='z') return sign-'a'; else if(sign=='|') return 26; } void insert(char s[],int k) { int u=0; int mx=strlen(s); for(int i=0;s[i];i++) { int c=idx(s[i]); if(!ch[u][c]) { memset(ch[sz],0,sizeof(ch[sz])); ch[u][c]=sz++; }u=ch[u][c]; } dep[u]=mx; if(!val[u]) val[u]=cnt++; ask[k]=val[u]; } void build() { f[0]=0; queue<int>q; for(int i=0;i<sigma;i++) { if(ch[0][i]) { f[ch[0][i]]=0; q.push(ch[0][i]); last[ch[0][i]]=0; } } while(!q.empty()) { int now=q.front(); q.pop(); for(int i=0;i<sigma;i++) { int son=ch[now][i]; if(!son) { ch[now][i]=ch[f[now]][i]; continue; } q.push(son); f[son]=ch[f[now]][i]; last[son]=val[f[son]]?f[son]:last[f[son]]; } } } void find(char *s) { int u=0,left=0,right=0,mid=0; for(int i=0;s[i];i++) { if(s[i]=='#') left=i; else if(s[i]=='|') mid=i; right=i; int c=idx(s[i]); u=ch[u][c]; if(val[u]&&dep[u]<=mid-left)//特殊判断操作,当前后缀之和小于等于原单词长度才进行计数 print(u); else print(last[u]); } } void print(int u) { if(u) { res[val[u]]++; print(last[u]); } }}ac;int main(){// freopen("1001.in","r",stdin);// freopen("out.txt","w",stdout); int t,n,q; scanf("%d",&t); while(t--) { memset(res,0,sizeof(res)); ac.clear(); scanf("%d%d",&n,&q); int cont=1; que[0]='#'; for(int i=0;i<n;i++) { scanf("%s",in); int len=strlen(in); for(int j=0;j<len;j++) que[cont+j]=in[j]; que[cont+len]='|'; cont=cont+len+1; for(int j=0;j<len;j++) que[cont+j]=in[j]; que[cont+len]='#'; cont=cont+len+1; } que[cont]=0; for(int i=1;i<=q;i++) { scanf("%s",head); scanf("%s",tail); int len=strlen(tail); tail[len]='|';len++; int len2=strlen(head); for(int j=0;j<len2;j++) tail[len+j]=head[j]; tail[len+len2]=0; ac.insert(tail,i); } ac.build(); ac.find(que); for(int i=1;i<=q;i++) printf("%d\n",res[ask[i]]); } return 0;}
阅读全文
1 0
- HDU 6096 (String) AC自动机
- hdu 6096 String(AC自动机)
- HDU 6096 String(AC自动机)
- HDU 6096 String AC自动机(多种解法)
- hdoj-6096String(AC自动机)
- hdu6096 String AC自动机
- hdu6096 String【AC自动机】
- 2017多校联合第六场String/hdu 6096 (tire tree/ac自动机)
- HDU--3407[String-Matching Automata] AC自动机或kmp
- hdu 6086 Rikka with String ac自动机+dp
- Hdu-6086 Rikka with String(AC自动机+DP)
- hdu 6086 -- Rikka with String(AC自动机 + 状压DP)
- HDU 6096:妙用AC自动机(666)
- AC自动机 hdu 2222
- HDU 2222(AC 自动机)
- hdu 2222 AC自动机
- hdu 3065(AC自动机)
- HDU 2222 AC自动机
- RabbitMQ日志无法禁用问题
- 解决提示:Invalid floating point operation.无效的浮点运算
- 【Spring】16、注解事务 @Transactional
- Mysql中limit的用法详解
- 计数游戏
- HDU 6096 (String) AC自动机
- mysqli事务实例
- Java基础加强总结(一)——注解(Annotation)
- 元素的隐藏和显示(v-show指令)
- Linux 系统结构
- 一个Linux内核利用init_task进行进程管理的简单例子
- 01背包问题 (好难理解)
- fedora18系统中安装Apache+PHP+MySQL环境
- POJ