返回顶部
扩大
缩小

BZOJ1212 [HNOI2004]L语言

看到最长前缀想到Trie字典树

但此题是分段匹配(多个单词),于是想到二重循环枚举起点

记录\(mark_i\)表示能否通过单词匹配到\(i\),如果可以则记录答案并以该点为起点继续匹配

若匹配过程中发现之前单词的结尾(可用结尾标记判断),则令当前\(mark=1\)

注:\(1MB\approx10^6\)

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

bool bo[1000005];//1MB=1e6
int ch[1000005][30],n,m,tot = 1;
char str[1000005];
bool mark[1000005];//表示能通过多个单词匹配到i位置 
inline void write(int s)
{
	if(s < 0)
	{
		putchar('-');
		s = -s;
		write(s);
	}
	if(s > 9)
		write(s % 10);
	putchar(s % 10 + '0');
}
inline void Insert(char * s)//建Trie字典树 
{
	int u = 1,len = strlen(s + 1);
	for(int i = 1;i <= len; ++i)
	{
		int c = s[i] - 'a';
		if(!ch[u][c])
			ch[u][c] = ++tot;
		u = ch[u][c];
	}
	bo[u] = true;
}
inline void find(char * s)//匹配文章s 
{
	int ans,now = 1,len = strlen(s + 1);
	memset(mark,0,sizeof mark);
	mark[0] = 1;//0位置匹配根节点 
	for(int i = 0,k;i <= len; ++i)//从0开始! 
	{
		if(!mark[i])//不能匹配到当前位置 
			continue;
		else
			ans = i;//记录答案 
		for(k = i + 1,now = 1;k <= len; ++k)
		{
			int c = s[k] - 'a';
			now = ch[now][c];
			if(!now)
				break;
			if(bo[now])//发现单词结尾 
				mark[k] = 1;
		}
	}
	cout << ans << endl;
}

int main()
{
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for(int i = 1;i <= n; ++i)
	{
		cin >> str + 1;
		Insert(str);
	}
	for(int i = 1;i <= m; ++i)
	{
		cin >> str + 1;
		find(str);
	}
	return 0;
}
posted @ 2019-11-20 21:36  Asasino  阅读(81)  评论(0编辑  收藏  举报