洛谷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;
}
相关文章
相关标签/搜索