在《信息學奧賽一本通提升篇》中 Trie字典樹 的課後練習看到這道題
而後我就用 Trie字典樹 作了這道題
據說這道題的正解是 AC自動機,數據跑滿時其餘的算法均可以卡掉
然而數據沒那麼強,我終究是過了ios
給定 \(n\) 個詞彙,\(m\) 個語句,每一個語句由若干個詞彙連續構成,每一個詞彙由若干個字符連續構成
對於一個詞彙,當且僅當這個詞彙能被完整的識別,也就是屬於給定的詞彙中的一個時,咱們稱 已理解此詞彙
對於一個語句,當且僅當其中的任意一個詞彙以前的全部詞彙都已被理解時,才能夠開始識別此詞彙
如今對於每一個語句,要求輸出其最後一個能被理解的詞彙的末尾位置的下標算法
這裏講一下如何用踹樹去作這道題
首先看樣例spa
4 3 is name what your whatisyourname whatisyouname whaisyourname
這是給定的詞彙和語句,思考一下該如何去從頭識別每個語句中的詞彙
根據踹樹的原理可知,咱們能夠以每一個給定詞彙爲一個分支,以每一個字符做爲轉移條件,建一棵踹樹
具體如圖所示:
每次從根節點開始向下遍歷,每理解一個詞彙計數器就更新
若遍歷出錯,則直接返回答案
若已遍歷到葉節點顯示還未出錯,則返回根節點找下一個詞彙,直到出錯或者整個語句已所有被理解
而後就能夠極慢地找出每一個語句能被理解到的最末位置code
在此基礎上,咱們維護兩個 \(map\),一個記錄當前語句是否被理解過,另外一個統計當前語句能被理解到的最末位置
緣由是在某些狀況下,一樣的運算步驟可能會重複不少遍,但使用映射 \(map\) 就能夠解決這個問題,至關於遞歸時的記憶化
當第一個 \(map\) 顯示當前語句已被理解過期,直接用另外一個 \(map\) 輸出對應的最末位置,能夠避免再從新識別當前這個已經被理解過一次的語句
這樣就能夠使得這個時間複雜度很是差的作法稍微快一點blog
以上顯然是一個暴力的作法,純屬亂搞
然而踹樹自己就不是本題的正解,若是能過那就是由於數據過水
本着尊重《信息學奧賽一本通提升篇》的編者的原則,我才用他所指定的這個作法來作這道題
至於這道題的正解 AC自動機
我不會 蛤蛤遞歸
#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; }