2017 Multi-University Training Contest
来源:互联网 发布:淘宝店的名字怎么取名 编辑:程序博客网 时间:2024/06/02 06:39
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6138
题目意思:题目给出n个字符串后,会有m个询问,每个询问会输入两个数,记为x,y吧。
然后要求的是,x,y的最长公共字串长度,而且这个最长公共子串要是n个字符串中某个
字符串的前缀。
解题知识点是AC自动机。AC自动机原来是用来多模式匹配的,原来每个节点维护的值
有一个标记是否是一个完整的字符串,然而由于该题是要前缀长度,我们就要队节点维护
的值有所改进,每个节点维护其所代表的前缀的长度即可。
这样的话,我们可以用这n个字符串构建字典树,然后通过BFS找到每个节点对应的fail
指针,接下来,由于我们要找的是第x个字符串和第y个字符串的最长公共字串长度,且该字串
是n个字符串中某个字符串的前缀。因此我们需要用第x个字符串和第y个字符串在字典树上进行
一个匹配。当第x个字符串去匹配的时候,我们要做的就是把所有在匹配时经过的节点都标记一下。
代表这些节点到字典树的根所代表的字符串既是某个字符串的前缀,还在第x个字符串中。同理
我们让第y个字符串在按相同的方法跑一遍AC自动机,有所不同的是,第一个串跑完之后已经留下
标记,我们可以在第2个串跑的时候,直接通过检测标记来确定某个节点所代表的字符串是不是x,y
的共同子串。然后边检测边求解答案就可以了。
代码我是用数组实现AC自动机的,链表的害怕超时,并没有写。写数组的真费劲,感觉还是链表比较爽。
AC代码如下:
#include <iostream>#include <stdio.h>#include <string.h>#include <queue>using namespace std;const int allSon = 26;const int maxn = 100010;int node[maxn][allSon]; ///字典树节点int fail[maxn]; ///fail指针int len[maxn]; ///存放根到当前节点的字符串长度int sign[maxn][2]; ///存放标记,sign[maxn][0]存放第一个串跑AC自动机的标记,sign[maxn][1]放第二个。char patten[maxn]; ///存放文本串int LCS; ///存放最长公共字串长度int id; ///用来给节点编号int len1[maxn]; ///存放第i个字符串在patten中的开始位置int len2[maxn]; ///存在第i个字符串的长度///将模式串插入到字典树void insertPatten(int left,int right) ///左右边界{ int p = 0; ///0就是根节点 int index = left; while(index < right) { int lowercase = patten[index]-'a'; if(node[p][lowercase] == -1) ///对应的节点不存在,创建节点 { memset(node[id],-1,sizeof(node[id])); ///儿子节点置空 len[id] = len[p]+1; ///该节点代表的前缀长度是其父节点加1 fail[id] = -1; ///失败指针不存在 node[p][lowercase] = id++; ///相应位置存放节点编号 } p = node[p][lowercase]; index++; }}void build_AC_automaton(){ queue<int>qu; int p = 0; ///指向根节点 qu.push(p); while(!qu.empty()) { p = qu.front(); qu.pop(); for(int i = 0; i < allSon; i++) { if(node[p][i] !=-1) ///这个孩子存在 { if(p == 0) ///p是根节点,其fail指针指向根 { fail[node[p][i]] = 0; } else { int temp = fail[p]; while(temp != -1) { if(node[temp][i] != -1) { fail[node[p][i]] = node[temp][i]; break; } temp = fail[temp]; } if(temp == -1) fail[node[p][i]] = 0; } qu.push(node[p][i]); } } }}///在AC自动机中进行匹配void find_in_AC_automaton(int left,int right,int flag){ int p = 0; ///根节点 int index = left; LCS = -1; while(index < right) { int lowercase = patten[index]-'a'; while(node[p][lowercase]==-1 && p!=0) p = fail[p]; p = node[p][lowercase]; if(p == -1) p = 0; int temp = p; while(temp!=0 && sign[temp][flag] != flag) { sign[temp][flag] = flag; if(flag == 1) ///flag=1代表第一个串已经跑过AC自动机了,现在是第2个串 { ///代表这个前缀是两个串的公共子串,直接在第2个串跑AC自动机的时候计算答案 if(sign[temp][0]!=-1 && sign[temp][1]!=-1) { LCS = max(LCS,len[temp]); } } temp = fail[temp]; } index++; }}int main(){ int t,n,m,x,y; ///测试数据组数 scanf("%d",&t); while(t--) { scanf("%d",&n); memset(node[0],-1,sizeof(node[0])); len[0] = 0; fail[0] = -1; id = 1; memset(len1,0,sizeof(len1)); ///len1[i]保存第i个串的开始位置 memset(len2,0,sizeof(len2)); ///len2[i]保存第i个串的长度。 ///将所有字符串存到同一个数组中,节省空间,并通过这两个数组结合实现快速访问 int prelen = 0; for(int i = 1; i <= n; i++) { scanf("%s",patten+prelen); len2[i] = strlen(patten+prelen); ///第i串的串长 len1[i] = prelen; insertPatten(prelen,prelen+len2[i]); prelen += len2[i]; } build_AC_automaton(); scanf("%d",&m); while(m--) { scanf("%d%d",&x,&y); memset(sign,-1,sizeof(sign)); ///直接锁定所要查询的串在patten中的范围。 find_in_AC_automaton(len1[x],len1[x]+len2[x],0); ///跑两次AC自动机 find_in_AC_automaton(len1[y],len1[y]+len2[y],1); printf("%d\n",LCS); } } return 0;}
阅读全文
0 0
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- #2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- #2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- 2017 Multi-University Training Contest
- POJ 1426 Find The Multiple (数字太大,余数代替原数)
- hdu 1212 Least Common Multiple
- LintCode66
- 加快Android studio 编译
- Spring Cloud 注册中心在tomcat中部署
- 2017 Multi-University Training Contest
- LintCode68
- servlet和CGI
- wireshark 实用过滤表达式(针对ip、协议、端口、长度和内容)
- CentOS7安装Java8
- CentOS6.5定制系统iso制作
- sqoop概念和安装部署
- CodeFroces 429B Godsend (模拟)
- sdut 素数链表