POJ 4052 金华邀请赛I题
这题当时比赛的时候,题目看了好久,没看懂(英语6级没过的压力很大啊),当时想先敲E题(后来事实证明这个选择是错误的。。E题到现在都不知道哪出错了)
其实题目就是给出N个字符串,如果S2包含S1,如果S2在文章中的话,就只计算S2,而不计算S1.反之亦然。
比赛时苦苦纠结于到底什么是text finger print。看懂题目后这题就是一个AC自动机的水题。
直接上代码
#include<iostream> #include<cstdio> #include<queue> #include<string> #include<vector> #include<algorithm> #define id(x) ((x) - ('A')) #define MAXN 3000001 using namespace std; struct trie { trie *next[26]; trie *fail; int id; }tree[MAXN],*head; string finger[2504]; vector<int> sub[2504]; //记录第i个字符串是哪些字符串的子串 bool cmp(const string &p, const string &q)//先按字符串长度排序,因为稍长的字符串肯定不是稍短的字符串的子串 { return p.length() < q.length(); } int COUNT; int pow(int x) { if (x == 0)return 1; int sum = 1; for (int i(0); i<x; ++i) { sum *= 10; } return sum; } void ini() { COUNT = 1; head = &tree[0]; for (int i(0); i<2501; ++i) { sub[i].clear(); finger[i].clear(); } for (int i(0); i<26; ++i) { head->next[i] = NULL; } head->fail = head; head->id = 0; } void insert(const string &p,int cnt) { trie *temp = head; for (int i(0); i<p.length(); ++i) { if (temp->next[id(p[i])] == NULL) { temp->next[id(p[i])] = &tree[COUNT++]; for (int i(0); i<26; ++i) { tree[COUNT-1].next[i] = NULL; } tree[COUNT-1].fail = NULL; tree[COUNT-1].id = 0; } temp = temp->next[id(p[i])]; } temp->id = cnt + 1; temp = head; for (int i(p.length() - 1); i>=0; --i) { if (temp->next[id(p[i])] == NULL) { temp->next[id(p[i])] = &tree[COUNT++]; for (int i(0); i<26; ++i) { tree[COUNT-1].next[i] = NULL; } tree[COUNT-1].fail = NULL; tree[COUNT-1].id = 0; } temp = temp->next[id(p[i])]; } temp->id = cnt + 1; } void AC_Construct() { queue<trie*> Q; for (int i(0); i<26; ++i) { if (head->next[i] != NULL) { head->next[i]->fail = head; Q.push(head->next[i]); } } while (!Q.empty()) { trie *front = Q.front(); Q.pop(); for (int i(0); i<26; ++i) { if (front->next[i] != NULL) { Q.push(front->next[i]); trie *temp = front->fail; while (temp != head && temp->next[i] == NULL) { temp = temp->fail; } if (temp->next[i] != NULL) { front->next[i]->fail = temp->next[i]; } else { front->next[i]->fail = temp; } } } } } void deal(string &data, const string &s) //把带括号的字符串转化为不带括号的字符串 { for (int i(0); i<s.length(); ++i) { if (s[i] == '[') { vector<char> num; int j = i+1; while ('0' <= s[j] && s[j] <= '9')num.push_back(s[j++]); int cnt = 0; for (int k(0); k<num.size(); ++k) { cnt += (num[k] - '0')*pow(num.size() - k - 1); } while (cnt--) { data += s[j]; } i = j + 1; } else { data += s[i]; } } } void search(const string &data, int n, int &len) { bool vis[2504] = {false}; trie *temp = head,*p; for (int i(0); i<data.length(); ++i) { while (temp != head && temp->next[id(data[i])] == NULL) { temp = temp->fail; } if (temp->next[id(data[i])] != NULL) { temp = temp->next[id(data[i])]; vis[temp->id] = true; } /* p = temp->fail; //本来应该加上这段的,不过我也不知道,为什么不加也能AC while (p != NULL && p != head && !vis[p->id]) { vis[p->id] = true; }*/ } if (n != len) { //如果第i个字符串是n+1的子串的话,就把n+1放进sub[i]里面 for (int i(1); i<=n; ++i) { if (vis[i]) { sub[i].push_back(n+1); } } } else { for (int i(1); i<=len; ++i) { //文章中判断第i个字符串是否包含在里面,如果包含的话,就检查第i个字符串是哪些字符串的子串 if (vis[i]) { //然后看这些串是否也包含在文章里,如果包含的话,就把VIS[I]变成false for (int j(0); j<sub[i].size(); ++j) { if (vis[sub[i][j]]) { vis[i] = false; break; } } } } int cnt(0); for (int i(1); i<=len; ++i) { //检查每个字符串有多少包含在文章里 if (vis[i])++cnt; } len = cnt; } } int main() { int T; int n; cin>>T; while (T--) { cin>>n; string data,s; ini(); for (int i(0); i<n; ++i) { string temp; cin>>temp; deal(finger[i],temp); } sort(finger,finger+n,cmp); for (int i(0); i<n; ++i) { insert(finger[i],i); } AC_Construct(); for (int i(0); i<n; ++i) { search(finger[i],i,n); } cin>>data; deal(s,data); search(s,n,n); cout<<n<<endl; } return 0; }