HDU2896 (AC自动机)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2896
题意:给定n个小于200的串,字符范围是0~127,再给定m个10000以内串,求n在m中出现的情况,中文题。
题解:这题有几个点非常坑,第一,字符范围不用0~127,
第二:输入:
4
aaaa
aaa
aa
a
1
aaaa
输出:可以是任意的。输出 1 2 3 4 AC 输出 1 2 2 3 3 3 4 4 4 4 AC,输出 1 ,AC。。。。什么都AC!
不过,这题非常容易MLE,找了网上几份不同的代码,以前M:3000+K,T:0Ms过的,现在居然MLE。不知道为什么。
其它就是直接套模版了,因为要多次匹配m串,注意找过的不要标记就可以了。(这种情况下,有一些输出的会是如上 1 2 2 3 3 3 4 4 4 4 )
代码如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn = 70000+50; 7 int cppos[30]; 8 int js; 9 struct node{ 10 int Next[128];//每一个节点可以扩展到的字母 11 int fail; //每一个结点的失配指针 12 int count;//记录每一个可以构成单词的字符串 13 int pos; 14 void init()//构造 15 { 16 memset(Next,-1,sizeof(Next));//next初始化为-1,代表不连接任何值 17 fail = 0; //失配指针为空 18 count = 0;//一开始没有单词,为0. 19 pos=0; 20 } 21 }s[maxn]; 22 23 int sind;//记录结点的编号 24 char str[222];//模版串,“单词”、 25 char des[11111];//“文章” 26 int q[maxn],qin,qout;//队列 27 28 void cas_init()//在整个程序前构造root 29 { 30 s[0].init();//初始化头结点 31 sind = 1; //当前有一个结点 32 } 33 34 void ins(int p)//向树中插入字母 35 { 36 int len = strlen(str); 37 int i, j, ind; 38 for(i = ind = 0;i < len;i++) 39 { 40 j = str[i]-31;//求出字母在next中的编号 41 if(s[ind].Next[j]== -1)//如果不存在子节点j,则构造新的 42 { 43 s[sind].init();//初始化 44 s[ind].Next[j] = sind++;//连向当前结点,并sind++代表总结点数 45 } 46 ind = s[ind].Next[j]; //向下走 47 } 48 s[ind].count++;//增加离根结点这条路径上字符串的个数,一条路上可能不止一个单词 49 s[ind].pos=p; 50 } 51 52 void make_fail()//构造失配指针 53 { 54 qin = qout = 0;//初始化队列 55 int i,ind,ind_f; 56 for(i=0;i<128;i++) 57 { 58 if(s[0].Next[i]!=-1) 59 q[qin++]=s[0].Next[i];//先考虑根结点,和根结点相连的都入队 60 } 61 while(qin!=qout) 62 { 63 ind = q[qout++];//记录队首结点 64 for(i = 0;i < 128; i++)//遍历队首结点的Next 65 { 66 if(s[ind].Next[i]!=-1)//如果结点Next不为空 67 { 68 q[qin++] = s[ind].Next[i];//将儿子节点入队 69 ind_f = s[ind].fail;//记录节点的失配指针指向 70 while(ind_f>0 && s[ind_f].Next[i]==-1) 71 /*当失配指针不为root时,一直循环直到找到一个结点是儿子是i值或者到了root*/ 72 ind_f = s[ind_f].fail; 73 if(s[ind_f].Next[i]!=-1) //如果当前结点有儿子的话记录下来备用 74 ind_f = s[ind_f].Next[i]; 75 s[s[ind].Next[i]].fail = ind_f; //使当前结点的失配指针指向刚才记录的结点完成失配指针的寻找构造 76 } 77 } 78 } 79 } 80 81 int fd() 82 { 83 int ct = 0; //记录“单词的个数” 84 int di,i,ind ,p; //di为指向“文章”的指针,ind为指向失配结点的指针(即trie树中失配的指针) 85 int len = strlen(des);//文章的长度 86 for(di = ind =0; di < len ; di++) 87 { 88 i = des[di]-31; 89 while(ind>0 && s[ind].Next[i] == -1) 90 /*当ind指针不是root和找不到结点的儿子是i时一直找下去(类似于KMP中while循环)*/ 91 ind = s[ind].fail; //一直寻找失配指针 92 93 if(s[ind].Next[i]!= -1)//找到了合适的失配指针 94 { 95 ind = s[ind].Next[i]; //指向这个儿子的节点,更新ind的值进行下一次匹配 96 97 p = ind; //用p来临时代替ind 98 while(p>0 && s[p].count!=-1) 99 /* p>0 表示还没到root,count!=-1表示指针前还有单词*/ 100 { 101 if(s[p].count>0) 102 { 103 ct+=s[p].count;//加上有的单词的个数 104 cppos[js++]=s[p].pos; 105 } 106 //s[p].count = -1;//不重复计算,注意这里很重要 107 p = s[p].fail;//一直寻找失配指针 108 } 109 } 110 } 111 //for(int i=0;i<maxn;i++) 112 // s[i].count = toto[i]; 113 return ct;//返回单词个数 114 } 115 116 117 int main() 118 { 119 int n,m; 120 js=0; 121 cin>>n; 122 cas_init();//初始化trie树 123 for(int i=1;i<=n;i++) 124 { 125 cin>>str; 126 ins(i);//构造trie树 127 } 128 cin>>m; 129 make_fail(); //构造失配指针 130 int j=1,sum=0; 131 while(m--) 132 { 133 js=0; 134 memset(cppos,0,sizeof(cppos)); 135 cin>>des; 136 if(fd()) 137 { 138 sum++; 139 printf("web %d:",j); 140 sort(cppos,cppos+js); 141 for(int k=0;k<js;k++) 142 printf(" %d" ,cppos[k]); 143 printf("\n"); 144 } 145 j++; 146 } 147 printf("total: %d\n",sum); 148 149 return 0; 150 }
浙公网安备 33010602011771号