bzoj-3172 单词

原创 2015年07月07日 21:22:29

题意:

给出n个单词,求在这n个单词组成的文章中每个单词出现了多少次;

n<=200,n个字符串总长度大概是500000  (数据范围显然有误);


题解:

一开始考虑就是先建个自动机,然后对每个串匹配一次;

经过的所有节点 和后缀的cnt全部+1;

然后查询每个单词的危险结点的cnt值就是答案;

复杂度O(n*len+k*n*len)基本也是线性的,似乎可过;

结果我T了;

然后查了一下题解,有种更好的思路,不需要用串去在自动机上匹配;

就是利用fail指针反向来构建一颗fail树;

首先因为除了根结点每个结点都有fail指针所以是n-1条边;

而显然所以fail指针最后都会回到root,即反向后的fail树中root可以到达所有点;

n点n-1边并且可以从一个点到达所有,这就是一颗有向树了;

根据fail指针的特性,倘若从树上某点x向根走,经过的每个点所代表的字符串都是x的后缀;

而一个字符串在另一个中的条件可以说成是,这个字符串是另一个字符串的某前缀的后缀;

那么可以想到从一个串的所有前缀向上拓展,每个能经过的点的cnt都增加;

这似乎是个动态规划的样子,具体实现就是:

建立自动机时将路径每个点的cnt都+1   (作为这个字符串的前缀);

对每个fail建立一个反向边;

根据反向边做树形dp,姿势和求子树权值和是一样的;

cnt[x]=∑cnt[ son[x] ] +cnt[x],得到的cnt就是答案;

其实这步也可以记录宽搜时的BFS序,反过来枚举就是满足的;

然后!我交上去居然还是T!

然后!。。我发现我模板少敲一句话;

好了,解决之后上面的两种方法都是可以A的,只是时间不同   (分明就是卡时啊);

1036514 140142 3172 Accepted 158720 kb 10940 ms 1456 B 2015-07-07 21:34:16
1036511 140142 3172 Accepted 160680 kb 252 ms 1235 B 2015-07-07 21:33:15
就是如此了,手一滑的危机啊;


代码:


正解:

#include<queue>
#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 510000
using namespace std;
char a[201][N];
int next[N][27],fail[N],cnt[N],ans[201],q[N];
int st,en,root=1,tot=1;
int insert(char *s)
{
	int index,p;
	p=root;
	while(*s!='\0')
	{
		index=*s-'a';
		if(!next[p][index])
			next[p][index]=++tot;
		p=next[p][index];
		*s++;
		cnt[p]++;
	}
	return p;
}
void Build()
{
	int i,p,temp;
	q[st=en=1]=root;
	while(st<=en)
	{
		p=q[st++];
		for(i=0;i<26;i++)
		{
			if(next[p][i])
			{
				temp=fail[p];
				while(temp)
				{
					if(next[temp][i])
					{
						fail[next[p][i]]=next[temp][i];
						break;
					}
					temp=fail[temp];
				}
				if(!temp)	fail[next[p][i]]=root;
				q[++en]=next[p][i];
			}
		}
	}
}
void slove()
{
	while(en)
		cnt[fail[q[en]]]+=cnt[q[en--]];
}
int main()
{
	int n,i;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%s",a[i]);
		ans[i]=insert(a[i]);
	}
	Build();
	slove();
	for(i=1;i<=n;i++)
		printf("%d\n",cnt[ans[i]]);
	return 0;
}

歪解:

#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 510000
using namespace std;
queue<int>q;
char a[201][N];
int next[N][27],fail[N],cnt[N],ans[201],root=1,tot=1;
int insert(char *s)
{
	int index,p;
	p=root;
	while(*s!='\0')
	{
		index=*s-'a';
		if(!next[p][index])
			next[p][index]=++tot;
		p=next[p][index];
		*s++;
	}
	return p;
}
void Build()
{
	int i,p,temp;
	q.push(root);
	while(!q.empty())
	{
		p=q.front(),q.pop();
		for(i=0;i<26;i++)
		{
			if(next[p][i])
			{
				temp=fail[p];
				while(temp)
				{
					if(next[temp][i])
					{
						fail[next[p][i]]=next[temp][i];
						break;
					}
					temp=fail[temp];
				}
				if(!temp)	fail[next[p][i]]=root;
				q.push(next[p][i]);
			}
		}
	}
}
void query(char *s)
{
	int index,p,temp;
	p=root;
	while(*s!='\0')
	{
		index=*s-'a';
		while(!next[p][index]&&!p)
			p=fail[p];
		p=p?next[p][index]:root;
		temp=p;
		while(temp)
		{
			cnt[temp]++;
			temp=fail[temp];
		}
		s++;
	}
}
int main()
{
	int n,i;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%s",a[i]);
		ans[i]=insert(a[i]);
	}
	Build();
	for(i=1;i<=n;i++)
		query(a[i]);
	for(i=1;i<=n;i++)
		printf("%d\n",cnt[ans[i]]);
	return 0;
}


循环单词

一,问题描述: 1,如果一个单词通过循环右移获得的单词,我们称这些单词都为一种循环单词。例如:picture和turepic就是属于同一种循环单词。现在给出n个单词,需要统计这n个单词中有多少种循环...
  • wodedipang_
  • wodedipang_
  • 2017年03月09日 11:34
  • 1186

vim的复制当前单词

在普通模式下,可使用 yaw  命令复制当前光标所在单词
  • riag
  • riag
  • 2009年05月14日 17:14
  • 17971

002_006 Python 处理文件中的每个单词

代码如下: #encoding=utf-8 print '中国' #处理文件中的每个单词 假定词由空格分开 ''' D:\123.txt的内容如下: 1 a b c 中 国 2 a b c 中 ...
  • houyj1986
  • houyj1986
  • 2014年03月14日 20:05
  • 2012

[TJOI2013]单词

题目大意一篇文章由nn个单词组成。询问每个单词在文章中作为某单词(包括自己)的子串总共出现了多少次(如果在某单词中多次出现,算多次)? 单词都由小写字母组成。令单词总长为LL,L≤1000000L\...
  • a_crazy_czy
  • a_crazy_czy
  • 2016年05月06日 22:03
  • 1011

用C++实现单词分割

题目:假定有如下语句行“I am happy today”,要求以空格为分隔符实现单词的分割。...
  • fduan
  • fduan
  • 2011年05月11日 10:16
  • 2834

【LeetCode】Word Break 单词拆分

题目Word Break Given a string s and a dictionary of words dict, determine if s can be segmented int...
  • xiangshimoni
  • xiangshimoni
  • 2015年04月24日 14:17
  • 2171

JS正则提取每个单词

  • qq_31070475
  • qq_31070475
  • 2017年02月21日 21:58
  • 572

mysql 绝对单词匹配

不是like单词 而是完全匹配一个单词 SELECT a.term_id cat_1, b.parent cat_2, a. NAME FROM mot_terms a LEFT JOIN ...
  • Q1059081877Q
  • Q1059081877Q
  • 2015年10月31日 17:31
  • 794

剑指offer----翻转单词顺序列----java实现

牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a a...
  • snow_7
  • snow_7
  • 2016年08月15日 16:35
  • 886

单词匹配算法

题目:有一个江洋大盗,他每次写信都是从一张报纸上剪下单词,再把单词贴在信上。假如某张报纸的单词保存在vectorpaper 中,而信的单词保存在vectorletter 中,写一个算法程序判别该报纸可...
  • u011263794
  • u011263794
  • 2015年10月13日 15:25
  • 926
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:bzoj-3172 单词
举报原因:
原因补充:

(最多只允许输入30个字)