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;
}
浙公网安备 33010602011771号