UVa 10679-I Love Strings!!

問題概要

100000文字以下の文字列Sが与えられる.Q個の1000文字以下の文字列に対しSの部分文字列となっているか判定せよ.

解法

Aho-Corasick法
Spagetthi Sourceさんを参照

実装(C++)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;

#define REP(i,x) for(int i=0;i<(int)x;i++)

struct PMA{
	PMA* next[256]; //失敗時に0を利用
	vector<int> matched;
	PMA(){REP(i,256)next[i]=0;}
	~PMA(){REP(i,256)if(next[i])delete next[i];}
};
//パターンマッチングオートマトンの生成.生成元パターンをpattern,個数をcountに代入して用いる
PMA *buildPMA(char *pattern[],int count){
	PMA *root=new PMA,*now;
	root->next[0]=root;
	//Phase1.Trie木の生成
	REP(i,count){
		now=root;
		for(int j=0;pattern[i][j]!='\0';j++){
			if(now->next[(int)pattern[i][j]]==0)now->next[(int)pattern[i][j]]=new PMA;
			now=now->next[(int)pattern[i][j]];
		}
		now->matched.push_back(i);
	}

	queue<PMA*> que;
	//Phase2.幅優先探索によるオートマトンの生成.
	for(int i=1;i<256;i++){
		if(!root->next[i])root->next[i]=root; //使われていない部分のnextをrootに
		else{
			root->next[i]->next[0] = root; //失敗時はルートに戻る
			que.push(root->next[i]); 
		}
	}
	while(!que.empty()){
		now=que.front();que.pop();
		for(int i=1;i<256;i++){
			if(now->next[i]){
				PMA *next=now->next[0];
				while(!next->next[i])next=next->next[0];
				now->next[i]->next[0]=next->next[i];
				que.push(now->next[i]);
			}
		}
	}
	return root;
}

void match(PMA *pma,const char *s,vector<int> &res){
	for(int i=0;s[i]!='\0';i++){
		int c=s[i];
		while(!pma->next[c]){
			pma=pma->next[0];
		}
		pma=pma->next[c];
		REP(j,pma->matched.size()){
			res[pma->matched[j]]=true;
		}
	}
}
char S[110000];
char *T[1000];
int main() {
	REP(i,1000)T[i]=new char[1005];
	int TCASE;
	scanf("%d",&TCASE);
	for(;TCASE--;){
		scanf("%s",S);
		int q;scanf("%d",&q);
		REP(i,q){
			scanf("%s",T[i]);
		}
		PMA *root=buildPMA(T,q);
		vector<int> res(q);
		match(root,S,res);
		REP(i,q){
			puts(res[i]?"y":"n");
		}
	}
	return 0;
}