1. 程式人生 > >bzoj 1590: [Usaco2008 Dec]Secret Message 祕密資訊

bzoj 1590: [Usaco2008 Dec]Secret Message 祕密資訊

簡述題意:

給你n個字串,再給你m個字串,詢問這m個字串中有多少成為n個字串的子串(或n個字串中有多少成為m個字串的子串)

演算法:trie樹

難度:NOIP

時間複雜度:O(n^2)

題解:就是裸跑trie樹,注意統計答案的方式!

sum表示以這個結點結尾的字串的個數;

summ表示經過此結點的次數;

程式碼如下:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
#define N 50005
#define M 500005//注意陣列大小!!! 
using namespace std;
int sum[M],summ[M];
int trie[M][5];
int cnt=1,k;
int str[N];
void insert(int m[])
{
	int now=0;
	for(int i = 1;i <= k;i++)
	{
		int tem=m[i];
		if(trie[now][tem]==0)
		{
			trie[now][tem]=cnt++;
		}
		now=trie[now][tem];
		summ[now]++;
	}
	sum[now]++;
}
int check(int m[])
{
    int ret=0;
    int now=0;
    bool fla=0;
	for(int i = 1;i <= k;i++)
    {
    	int temp=m[i];
    	if(!trie[now][temp])
    	{
	    	fla=1;
	    	break;
	    }
        ret+=sum[now];//① 
        now=trie[now][temp];//② 
    }
    ret+=sum[now];//如果①位置與②位置調換,則不用寫這句話,否則最後一個位置的sum沒加到ret上!
    if(!fla)
	{
		ret+=summ[now];
        ret-=sum[now];
	} 
    return ret;
}
int main()
{
	int n,km;
	scanf("%d%d",&km,&n);
	for(int i = 1;i <= km;i++) 
	{
        scanf("%d",&k);
        for(int j = 1;j <= k;j++)
		{
			scanf("%d",&str[j]);
        }
        insert(str);
    }
    for(int i = 1;i <= n;i++)
	{
        scanf("%d",&k);
        for(int j = 1;j <= k;j++)
		{
			scanf("%d",&str[j]);
        }
        printf("%d\n",check(str));
    }
	return 0 ;
}