洛谷 P2922 [USACO08DEC]祕密消息Secret Message

題目

題目大意

信息是二進制的,共有M條。

約翰已經攔截了這些信息,知道了第i條二進制信息的前b_i位。

他同時知道,奶牛使用N條密碼。

但是,他僅瞭解第j條密碼的前c_j位。

對於每條密碼j,他想知道有多少截得的信息能夠和它匹配。

也就是說,有多少信息和這條密碼有着相同的前綴。

樣例解釋

4條信息,5條密碼

截獲的信息前綴是010,1,100,110。

可能的密碼前綴是0,1,01,01001,11。

0只配對010;

1配對1,100,110;

01只配對010;

01001配對010;

11配對1,110。

題目分析

根據題意:answer=該密碼作爲別的信息前綴+別的信息作爲該密碼前綴的情況。

讓我們分類討論一下(滑稽)

1、該密碼作爲前綴

加入一個變量tr[i].s記錄經過該結點的單詞數量。

即從根節點到這個結點路徑一模一樣。

即有多少個單詞的前綴一樣。

那麼該密碼跑到最後的tr[i].s即爲該密碼作爲別的信息前綴的情況。

2、別的信息作爲該密碼前綴

即跑的時候注意經過所有結點的end相加。

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int n,m,len=0;
struct node{int son[10],end,s;}tr[1000010];

void bt(int l)
{
	int now=0;
	for(int i=1;i<=l;i++)
	{
		int x; scanf("%d",&x);
		if(tr[now].son[x])
		{
			now=tr[now].son[x];
			tr[now].s++;
		}
		else
		{
			len++; tr[now].son[x]=len;
			now=len; tr[now].end=0;
			tr[now].s++;
			tr[now].son[0]=tr[now].son[1]=0;
		}
	}
	tr[now].end++;
}

int find(int l)
{
	bool gg=false;
	int now=0,s=0;
	for(int i=1;i<=l;i++)
	{
		int x; scanf("%d",&x);
		if(gg) continue;
		if(tr[now].son[x])
		{
			now=tr[now].son[x];
			s+=tr[now].end;//信息作爲該密碼的前綴 
		}
		else gg=true;
		//沒有人跟他配了找下去也沒有意義啊 
	}
	if(gg) return s;
	return s+(tr[now].s-tr[now].end);
	//答案還要加上該密碼作爲別的信息前綴的情況 
	//如果該密碼與別的信息相同,已經作爲前綴算過一遍了 
	//所以要 -tr[now].end
	//tr[now].s記錄 經過該結點的單詞數量 即從根節點到這個結點路徑一模一樣 
	//即有多少個單詞的前綴一樣 
}

int main()
{
	tr[0].son[0]=tr[0].son[1]=0;
	scanf("%d%d",&n,&m);
	while(n--)
	{
		int k; scanf("%d",&k);
		bt(k);
	}
	while(m--)
	{
		int k; scanf("%d",&k);
		printf("%d\n",find(k));
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章