HDU 6096 String (AC自动机)
Posted dwtfukgv
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 6096 String (AC自动机)相关的知识,希望对你有一定的参考价值。
题意:给出n个字符串和q个询问,每次询问给出两个串 p 和 s 。要求统计所有字符串中前缀为 p 且后缀为 s (不可重叠)的字符串的数量。
析:真是觉得没有思路啊,看了官方题解,真是好复杂。
假设原始的字符串 数组为A,首先将A中的每个字符串都进行翻转,得到字符串数组B,然后,将A和B按字典序排序。
对于一个查询来说有一个前缀p和后缀s, 所有包含前缀p的字符串在A中是连续的,可通过二分求出该区间 设为[Lp,Rp],同样,所有包含后缀s的字符串在B中也是连续的,设为[Ls,Rs]
接下来只需求解 有多少个字符串前缀是在[Lp,Rp] 同时后缀在[Ls,Rs]。对于每个字符串,假设在A中是第x个,在B中是第y个 ,那么我们只需要判断有多少个字符串 Lp<=x<=Rp 同时 Ls<=y<=Rs
该问题转化为,有一些点(每个字符串相当于一个点,x是按前缀排完序的位置,y是按后缀排序),现给定一些矩形(每个查询可转化为 Lp<=x<=Rp,Ls<=y<=Rs),问矩形中包含多少个点,该问题是经典的矩形覆盖问题,线段树+扫描线 即可求出。
按上述方法求出后,会存在重叠的问题 。如有一个字符串 aaa 查询如果为 aa aa的话也会查到 aaa。 那么我们需要进行去重,可直接对查询的前缀或者后缀做一个遍历,枚举重叠的长度,然后再哈希判断是否存在这样的原始字符串即可。
时间复杂度 O(n\log(n)+|S|)O(nlog(n)+∣S∣)
避免hash可以离线暴力在字典树上建线段树,查询在字典树上找到后缀对应节点查找前缀区间和。空间O(|S|log(n))O(∣S∣log(n))。时间复杂度 O(n\log(n)+|S|)O(nlog(n)+∣S∣)
也可以直接hash离线做。
后来还是在网上看到了大佬们的解法,真是奇妙。
离线操作,先把前缀和后缀通过 s + ‘{‘ + p,作为模板插入到AC 自动机中去,然后再对原来的文本进行查询,先把文本构造成(假设文本是abcd) abcd{abcd,这样的就可以使用AC自动机进行查询了,还要判断是不是重叠,这可以通过比较字符串的长度来判断。注意去重,因为没有去重WA到死、、、
代码如下:
#pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib> #include <cmath> #include <iostream> #include <cstring> #include <set> #include <queue> #include <algorithm> #include <vector> #include <map> #include <cctype> #include <cmath> #include <stack> #include <assert.h> #include <sstream> #define debug() puts("++++"); #define gcd(a, b) __gcd(a, b) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define freopenr freopen("in.txt", "r", stdin) #define freopenw freopen("out.txt", "w", stdout) using namespace std; typedef long long LL; typedef unsigned long long ULL; typedef pair<int, int> P; const int INF = 0x3f3f3f3f; const LL LNF = 0xffffffffffLL; const double inf = 0x3f3f3f3f3f3f; const double PI = acos(-1.0); const double eps = 1e-6; const int maxn = 100000 + 10; const int mod = 1e9 + 7; const int dr[] = {-1, 0, 1, 0}; const int dc[] = {0, 1, 0, -1}; const char *de[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"}; int n, m; const int mon[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; const int monn[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; inline bool is_in(int r, int c){ return r >= 0 && r < n && c >= 0 && c < m; } const int sigma = 27; const int maxnode = 2200000 + 1000; struct Aho{ int ch[maxnode][sigma]; int val[maxnode]; int f[maxnode], last[maxnode]; int ans[maxn], len[maxnode]; int sz; void init(){ sz = 1; memset(ch[0], 0, sizeof ch[0]); memset(ans, 0, sizeof ans); } int idx(char ch){ return ch - ‘a‘; } int insert(const char *s, int v){ int u = 0, i = 0; while(s[i]){ int c = idx(s[i]); if(!ch[u][c]){ memset(ch[sz], 0, sizeof ch[sz]); val[sz] = 0; ch[u][c] = sz++; } u = ch[u][c]; ++i; } if(val[u]) return val[u]; len[u] = i; return val[u] = v; } void getFail(){ queue<int> q; f[0] = 0; for(int c = 0; c < sigma; ++c){ int u = ch[0][c]; if(u){ f[u] = 0; q.push(u); last[u] = 0; } } while(!q.empty()){ int r = q.front(); q.pop(); for(int c = 0; c < sigma; ++c){ int u = ch[r][c]; if(!u){ 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] = val[f[u]] ? f[u] : last[f[u]]; } } } void query(const char *T, int n){ int j = 0; for(int i = 0; T[i]; ++i){ int c = idx(T[i]); j = ch[j][c]; if(val[j]) print(j, n); else if(last[j]) print(last[j], n); } } void print(int j, int n){ if(!j) return ; if(len[j] <= n) ++ans[val[j]]; print(last[j], n); } }; Aho aho; char *s[maxn]; char str[maxnode]; char s1[maxn], s2[maxn]; int len[maxn], pos[maxn]; int main(){ int T; cin >> T; while(T--){ scanf("%d %d", &n, &m); int cnt = 0; for(int i = 1; i <= n; ++i){ s[i] = str + cnt; scanf("%s", s[i]); len[i] = strlen(s[i]) + 1; cnt += len[i]; strcpy(str+cnt, s[i]); str[cnt-1] = ‘{‘; cnt += len[i]; } aho.init(); for(int i = 1; i <= m; ++i){ scanf("%s %s", s1+1, s2); s1[0] = ‘{‘; strcat(s2, s1); pos[i] = aho.insert(s2, i); } aho.getFail(); for(int i = 1; i <= n; ++i) aho.query(s[i], len[i]); for(int i = 1; i <= m; ++i) printf("%d\n", aho.ans[pos[i]]); } return 0; } /* 2 4 4 aba cde acdefa cdef a a cd ef ac a ce f 1 1 aaa aa aa */
以上是关于HDU 6096 String (AC自动机)的主要内容,如果未能解决你的问题,请参考以下文章
HDU - 6096 :String (AC自动机,已知前后缀,匹配单词,弱数据)