在《信息学奥赛一本通提升篇》中 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; }