【HDU2222】【AC自动机模板 测烂为止】Keywords Search

传送门:HDU222

描述:

Keywords Search

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 56072    Accepted Submission(s): 18307


Problem Description
In the modern time, Search engine came into the life of everybody like Google, Baidu, etc.
Wiskey also wants to bring this feature to his image retrieval system.
Every image have a long description, when users type some keywords to find the image, the system will match the keywords with description of image and show the image which the most keywords be matched.
To simplify the problem, giving you a description of image, and some keywords, you should tell me how many keywords will be match.
 

Input
First line will contain one integer means how many cases will follow by.
Each case will contain two integers N means the number of keywords and N keywords follow. (N <= 10000)
Each keyword will only contains characters 'a'-'z', and the length will be not longer than 50.
The last line is the description, and the length will be not longer than 1000000.
 

Output
Print how many keywords are contained in the description.
 

Sample Input
 
   
1 5 she he say shr her yasherhs
 

Sample Output
 
   
3
 

Author
Wiskey
 

Recommend
lcy   |   We have carefully selected several similar problems for you:   2896  3065  2243  2825  3341 
 
题意:

给你很多个单词,然后给你一篇文章,问给出的单词在文章中出现的次数。

思路:

AC自动机入门题。需要注意的就是可能有重复单词。这里提供两个模板

代码一:

(飘过的小牛的模板,时间稍快,不过内存消耗大)

//HDU 2222 G++ 327ms  58960k
//构造失败指针:设当前节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为C的。然后把当前节点的失败指针指向那个字母也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root。
//匹配(1)当前字符匹配,只需沿该路径走向下一个节点继续匹配即可;(2)当前字符不匹配,则去当前节点失败指针所指向的字符继续匹配.重复这2个过程中的一个,直到模式串走完。
#include 
using namespace std;
const int maxn=500005;
char s[1000010],key[51];
int head,tail;

struct node{  
  node *fail;  
  node *next[26]; //字母的字典序编号
  int cnt;  
  node(){//init    
    fail = NULL;  
    cnt = 0;  
    for(int i = 0; i < 26; ++i)  
      next[i] = NULL;  
  }  
}*q[maxn];  
node *root;

void inser(char *s){//建立trie树
	int tmp, len;  
  node *p = root;  
  len = strlen(s);  
  for(int i = 0; i < len; ++i){  
    tmp = s[i] - 'a';  
    if(p->next[tmp] == NULL)  
      p->next[tmp] = new node();  
      p = p->next[tmp];  
  }  
  p->cnt++;
}

void build(){ //初始化fail指针,BFS  
	q[tail++]=root;
	while(head!=tail){
		node *p=q[head++]; //弹出队头  
		node *tmp=NULL;
		for(int i=0; i<26; i++){
			if(p->next[i]!=NULL){
				if(p==root)//第一个元素fail必指向根  
					p->next[i]->fail=root;
				else{
					tmp=p->fail;//失败指针
					while(tmp!=NULL){//2种情况结束:匹配为空or找到匹配 
						if(tmp->next[i]!=NULL){//找到匹配  
							p->next[i]->fail=tmp->next[i];
							break;
						}
						tmp=tmp->fail;
					}
					if(tmp==NULL)
						p->next[i]->fail=root;
				}
				q[tail++]=p->next[i];//入队
			}
		}
	}
}

int query(){//扫描
  int index,len,res;  
  node *p=root; //Tire入口  
  res=0;  
  len=strlen(s); 
  for(int i=0; inext[index]==NULL && p!=root)//跳转失败指针  
  		p=p->fail;
  	p=p->next[index];
  	if(p==NULL)p=root;
  	node *tmp=p;//p不动,tmp计算后缀串 
  	while(tmp!=root && tmp->cnt!=-1){
  		res+=tmp->cnt;
  		tmp->cnt=-1;
  		tmp=tmp->fail;
  	}
  }
  return res;
}

int main(){
	int t,n;
	scanf("%d",&t);
	while(t--){
		head=tail=0;
		root=new node();
		scanf("%d",&n);
		getchar();
		for(int i=0; i

代码二:

(bin神的模板,内存消耗小)

//G++  764ms  27968k
//======================
// HDU 2222
// 求目标串中出现了几个模式串
//====================
#include
using namespace std;

const int maxn=500010;

struct Trie{
  int next[maxn][26],fail[maxn],end[maxn];//字母的字典序编号 失败指针 以此结点为末尾的单词个数
  int root,L;//根结点 总的结点数

  int newnode(){
    for(int i = 0;i < 26;i++)
      next[L][i] = -1;
    end[L++] = 0;
    return L-1;
  }

  void init(){
    L = 0;
    root = newnode();
  }

  void insert(char buf[]){//建立trie树
    int len = strlen(buf);
    int now = root;
    for(int i = 0;i < len;i++){
      if(next[now][buf[i]-'a'] == -1)
        next[now][buf[i]-'a'] = newnode();
      now = next[now][buf[i]-'a'];
    }
    end[now]++;
  }

  void build(){//初始化fail指针,BFS
    queueQ;
    fail[root] = root;
    for(int i = 0;i < 26;i++)//处理初始的元素
      if(next[root][i] == -1)
        next[root][i] = root;
      else{
        fail[next[root][i]] = root;//第一个元素fail必指向根
        Q.push(next[root][i]);
      }
    while( !Q.empty() ){
      int now = Q.front();
      Q.pop();
      for(int i = 0;i < 26;i++)
        if(next[now][i] == -1)
          next[now][i] = next[fail[now]][i];
        else{
          fail[next[now][i]]=next[fail[now]][i];
          Q.push(next[now][i]);
        }
    }
  }

  int query(char buf[]){//扫描文本串
    int len = strlen(buf);
    int now = root;
    int res = 0;
    for(int i = 0;i < len;i++){
      if(buf[i]>='a' && buf[i]<='z')now = next[now][buf[i]-'a'];//结合具体题目修改
      else if(buf[i]>='A' && buf[i]<='Z')now = next[now][buf[i]-'A'];
      else continue;
      int temp = now;
      while( temp != root ){
        res += end[temp];
        end[temp] = 0;
        temp = fail[temp];
      }
    }
    return res;
  }

  void debug(){//调试
    for(int i = 0;i < L;i++){
      printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
      for(int j = 0;j < 26;j++)
        printf("%2d",next[i][j]);
        printf("]\n");
    }
  }
};
char buf[1000010];
Trie ac;

int main(){
  int T;
  int n;
  scanf("%d",&T);
  while( T-- ){
    scanf("%d",&n);
    ac.init();////////
    for(int i = 0;i < n;i++){
      scanf("%s",buf);
      ac.insert(buf);////////
    }
    ac.build();
    scanf("%s",buf);//gets scanf 得看情况
    printf("%d\n",ac.query(buf));////////
  }
  return 0;
}




你可能感兴趣的:(online,judge,HDU,------AC自动机)