@lychee123
2017-07-14T04:10:51.000000Z
字数 2310
阅读 1633
数据结构
先给你一个不超过1000000长度的大串s;接下来输入一个n代表接下来输入的小串个数,小串长度不超过6。
小串分两种类型0和1类型。
0类型表示小串在大串中的最大匹配个数就是常规的AC自动机的做法。
1类型表示小串在大串中不能重合的最大匹配数。
依次输出结果.(所有的串只包含小写字母)
按样例输出,注意每组测试数据后有一个换行。
Sample Input
ab
2
0 ab
1 ababababac
2
0 aba
1 abaabcdefghijklmnopqrstuvwxyz
3
0 abc
1 def
1 jmn
Sample OutputCase 1
1
1Case 2
3
2Case 3
1
1
0
当为0类型时就是最基本的AC自动机的解法。
当为1类型时我们就要用数组len[]来记录一下该节点位置单词的长度。用lastpoint[]来记录这个节点位置单词上次出现的位置。在循环大串的时候直接判断 i - len[j] >= lastpoint[j] 是否满足。如果满足说明没有出现覆盖上一次出现的位置,此时这种类型的ans就++。
说明一下。
对于每个小串我们在getfind函数里面都求出0,1两种类型情况下的结果。
用ans[maxn][0],来记录所有小串为0类型时结果。
用ans[maxn][1],来记录所有小串为1类型时结果。
注意!!!
很多小细节应该仔细一点。case的kk已经写错两次了!!!!对于locate数组。locate数组存的是locate[i]表示第i个小串在树上的结尾节点。由于我们存的小串可能是相同的串,类型不同。如果还是照习惯用isword数组来存他是第几个串,那么相同的串的不同类型会被最后出现的串覆盖掉。所以先出现的小串则会出现找不到的情况。这样是不对的。
#include<bits/stdc++.h>using namespace std;const int maxn = 6e5 + 6;int last[maxn], f[maxn];int ch[maxn][26];int cnt;int isword[maxn];//这个点是单词int locate[maxn];//locate[i]表示第i个单词存在的节点位置int lastpoint[maxn];//lastpoint[i]表示i这个节点处的单词上一次出现的位置int len[maxn];//len[i]表示i这个节点处的单词的长度void insert(char s[], int id){int root = 0;int l = strlen(s);for(int i = 0; i < l; i++){if(ch[root][s[i] - 'a'] == 0)ch[root][s[i] - 'a'] = ++cnt;root = ch[root][s[i] - 'a'];}isword[root] = 1;len[root] = l;locate[id] = root;//这个单词处在树上节点的标号lastpoint[root] = -INT_MAX / 2;//由于单词长度不超过6 所以小于-7都OK}void getfail(){queue<int>q;f[0] = 0;for(int i = 0; i < 26; i++){int u = ch[0][i];if(u != 0){f[u] = 0;q.push(u);last[u] = 0;}}while(!q.empty()){int r = q.front();q.pop();for(int c = 0; c < 26; c++){int u = ch[r][c];if(u == 0){ch[r][c] = ch[f[r]][c];continue;}q.push(u);int v = f[r];while(v && !ch[v][c])v = f[v];f[u] = ch[v][c];last[u] = isword[f[u]] ? f[u] : last[f[u]];}}}int ans[maxn][3];void getfind(char T[]){memset(ans, 0, sizeof(ans));int n = strlen(T);int j = 0;for(int i = 0; i < n; i++){j = ch[j][T[i] - 'a'];if(isword[j]){ans[j][0]++;if(i - len[j] >= lastpoint[j]){ans[j][1]++;lastpoint[j] = i;}}int temp = last[j];while(temp){ans[temp][0]++;if(i - len[temp] >= lastpoint[temp]){ans[temp][1]++;lastpoint[temp] = i;}temp = last[temp];}}}char s[maxn];int type[maxn];char s1[maxn];int main(){int kk = 0;//这种小细节不要再出问题了!!!!!!!!!while(~scanf("%s", s)){memset(ch, 0, sizeof(ch));memset(isword, 0, sizeof(isword));int n;cnt = 0;scanf("%d", &n);for(int i = 1; i <= n; i++){scanf("%d%s", &type[i], s1);insert(s1, i);}getfail();getfind(s);printf("Case %d\n", ++kk);for(int i = 1; i <= n; i++){printf("%d\n", ans[locate[i]][type[i]]);}puts("");}return 0;}