洛谷P2292

在《信息學奧賽一本通提升篇》中 Trie字典樹 的課後練習看到這道題
而後我就用 Trie字典樹 作了這道題
據說這道題的正解是 AC自動機,數據跑滿時其餘的算法均可以卡掉
然而數據沒那麼強,我終究是過了ios

Description

給定 \(n\) 個詞彙,\(m\) 個語句,每一個語句由若干個詞彙連續構成,每一個詞彙由若干個字符連續構成
對於一個詞彙,當且僅當這個詞彙能被完整的識別,也就是屬於給定的詞彙中的一個時,咱們稱 已理解此詞彙
對於一個語句,當且僅當其中的任意一個詞彙以前的全部詞彙都已被理解時,才能夠開始識別此詞彙
如今對於每一個語句,要求輸出其最後一個能被理解的詞彙的末尾位置的下標算法

Solution

這裏講一下如何用踹樹去作這道題
首先看樣例spa

4 3 
is
name
what
your
whatisyourname
whatisyouname
whaisyourname

這是給定的詞彙和語句,思考一下該如何去從頭識別每個語句中的詞彙
根據踹樹的原理可知,咱們能夠以每一個給定詞彙爲一個分支,以每一個字符做爲轉移條件,建一棵踹樹
具體如圖所示:

每次從根節點開始向下遍歷,每理解一個詞彙計數器就更新
若遍歷出錯,則直接返回答案
若已遍歷到葉節點顯示還未出錯,則返回根節點找下一個詞彙,直到出錯或者整個語句已所有被理解
而後就能夠極慢地找出每一個語句能被理解到的最末位置code

在此基礎上,咱們維護兩個 \(map\),一個記錄當前語句是否被理解過,另外一個統計當前語句能被理解到的最末位置
緣由是在某些狀況下,一樣的運算步驟可能會重複不少遍,但使用映射 \(map\) 就能夠解決這個問題,至關於遞歸時的記憶化
當第一個 \(map\) 顯示當前語句已被理解過期,直接用另外一個 \(map\) 輸出對應的最末位置,能夠避免再從新識別當前這個已經被理解過一次的語句
這樣就能夠使得這個時間複雜度很是差的作法稍微快一點blog

Other things

以上顯然是一個暴力的作法,純屬亂搞
然而踹樹自己就不是本題的正解,若是能過那就是由於數據過水
本着尊重《信息學奧賽一本通提升篇》的編者的原則,我才用他所指定的這個作法來作這道題
至於這道題的正解 AC自動機
我不會 蛤蛤遞歸

Code

#include<map>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 200010
#define LL long long
#define uLL unsigned long long

using namespace std;

int n,m,ans;
char s[25],S[maxn];
map<string,int> Get;
map<string,bool> Judge;

inline int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
    return s*w;
}

struct Trie{
    int Nxt[maxn][25],cnt;
    bool flag[maxn],vis[maxn];
    void Insert(char *s){
	int p=0,len=strlen(s+1);
	for(int i=1;i<=len;i++){
	    int c=s[i]-'a';	
	    if(!Nxt[p][c]) Nxt[p][c]=++cnt;
   	        p=Nxt[p][c];
	}
	flag[p]=1;
    }
	
    int Find(char *s){
	int p=0,len=strlen(s+1);
	if(Judge[s+1]) return Get[s+1];
	memset(vis,false,sizeof vis);vis[0]=true;
	for(int i=0;i<=len;i++){
	    if(!vis[i]) continue;cnt=i;
	    for(int j=i+1;j<=len;j++){
	        int c=s[j]-'a';
		p=Nxt[p][c];if(!p) break;
	        if(flag[p]) vis[j]=true;
	    }
	}
	Judge[s+1]=true;Get[s+1]=cnt;
	return cnt;
    }
}Tri;

int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++){
	scanf("%s",s+1);
	Tri.Insert(s);
    }
    for(int i=1;i<=m;i++){
	scanf("%s",S+1);
	printf("%d\n",Tri.Find(S));
    }
    return 0;
}
相關文章
相關標籤/搜索