zoukankan      html  css  js  c++  java
  • [HNOI2004]L语言 trie树? Ac自动机? hash!!

    题目描述
    标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的。现在你要处理的就是一段没有标点的文章。

    一段文章T是由若干小写字母构成。一个单词W也是由若干小写字母构成。一个字典D是若干个单词的集合。我们称一段文章T在某个字典D下是可以被理解的,是指如果文章T可以被分成若干部分,且每一个部分都是字典D中的单词。

    例如字典D中包括单词{‘is’, ‘name’, ‘what’, ‘your’},则文章‘whatisyourname’是在字典D下可以被理解的,因为它可以分成4个单词:‘what’, ‘is’, ‘your’, ‘name’,且每个单词都属于字典D,而文章‘whatisyouname’在字典D下不能被理解,但可以在字典D’=D+{‘you’}下被理解。这段文章的一个前缀‘whatis’,也可以在字典D下被理解,而且是在字典D下能够被理解的最长的前缀。

    给定一个字典D,你的程序需要判断若干段文章在字典D下是否能够被理解。并给出其在字典D下能够被理解的最长前缀的位置。

    输入格式
    输入文件第一行是两个正整数n和m,表示字典D中有n个单词,且有m段文章需要被处理。之后的n行每行描述一个单词,再之后的m行每行描述一段文章。

    其中1<=n, m<=20,每个单词长度不超过10,每段文章长度不超过1M。

    输出格式
    对于输入的每一段文章,你需要输出这段文章在字典D可以被理解的最长前缀的位置。

    emmmmmm, 好多神奇的算法, 什么KMP, 什么trie树, 什么AC自动机, 但是都不会, 题解竟然没有hash, 还是用hash + dp, 随便搞一搞吧。。。

    刚看到这道题的时候, 这不显然用hash;用s, t表示两个指针, 不断把t++, 如果([s, t])这个区间能够满足, 就把s赋成t + 1, 最后输出s - 1, 然后。。。我就光荣的WA掉了。 不妨看这组数据:

    a
    abc
    
    aabc
    

    假如用以上算法的话输出值为2, 即匹配了aa, 但整个文章显然都是可以翻译的, 所以上面贪心的思想就是不对的, 那怎么办呢。。。

    首先我把每个单词的hash值求出来, 再用一次数字hash, 保证这个hash值能在我们存储的范围内。 并且我开一个vector, q[i]表示以i结尾的单词的hash值和长度。

    在匹配的时候, 这(f[i])表示前缀i是否能匹配, 我从1枚举到当前文章的长度len, 并枚举以当前字母结尾的hash值和长度len1, 当且仅当(f[i - len1])能满足, 且([i - len1 + 1, i])这个区间是一个单词, 那么(f[i])就可以赋成true, 这样输出最后能够达到的长度即可。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef unsigned long long ull;
    const int INF = 0x3f3f3f3f;
    const int MAXN = 1e6 + 100;
    const int MAXM = 5e3 + 10;
    const double eps = 1e-5;
    
    template < typename T > inline void read(T &x) {
    	x = 0; T ff = 1, ch = getchar();
    	while (!isdigit(ch)) {
    		if (ch == '-') ff = -1;
    		ch = getchar();
    	}
    	while (isdigit(ch)) {
    		x = (x << 1) + (x << 3) + (ch ^ 48);
    		ch = getchar();
    	}
    	x *= ff;
    }
    
    template < typename T > inline void write(T x) {
    	if (x == 0) {
    		putchar('0');
    		return ;
    	}
    	if (x < 0) putchar('-'), x = -x;
    	static T tot = 0, ch[30];
    	while (x) {
    		ch[++tot] = x % 10 + '0';
    		x /= 10;
    	}
    	while (tot) putchar(ch[tot--]);
    } 
    
    ull p[MAXN], vis[MAXN], sum[MAXN];
    int n, m, f[MAXN];
    char ch[MAXN];
    const int mod = 1e6 + 7;
    vector < pair < int, int > > q[30]; 
    
    inline int H(ull x) {
    	int u = x % mod;
    	while (vis[u] && vis[u] != x) ++u;
    	return u;
    }
    
    int main() {
    	read(n), read(m);
    	for (int i = 1; i <= n; ++i) {
    		scanf("%s", ch + 1);
    		int len = strlen(ch + 1);
    		ull cnt = 0;
    		for (int j = 1; j <= len; ++j) {
    			cnt = cnt * 131 + ch[j] - 'a' + 1;
    		}
    		int u = H(cnt);
    		vis[u] = cnt;
    		q[ch[len] - 'a' + 1].push_back({u, len});
    	}
    	p[0] = 1;
    	for (int i = 1; i <= MAXN; ++i) p[i] = p[i - 1] * 131;
    	while (m--) {
    		scanf("%s", ch + 1);
    		int len = strlen(ch + 1);
    		for (int i = 1; i <= len; ++i) 
    			sum[i] = sum[i - 1] * 131 + ch[i] - 'a' + 1;
    		f[0] = 1;
    		int cnt = 0;
    		for (int i = 1; i <= len; ++i) {
    			f[i] = 0;
    			int x = ch[i] - 'a' + 1;
    			for (int j = 0; j < q[x].size(); ++j) {
    				int u = q[x][j].first, len1 = q[x][j].second;
    				if (i < len1) continue;
    				if (!f[i - len1]) continue;
    				ull v = sum[i] - sum[i - len1] * p[len1];
    				int h = H(v);
    				if (vis[h]) f[i] = 1, cnt = i;
    			} 
    		}
    		write(cnt);
    		puts("");
    	}
     	return 0;
    }
    
    
  • 相关阅读:
    高放的c++学习笔记之函数基础
    高放的c++学习笔记之关联容器
    高放的c++学习笔记之lambda表达式
    二分图小结
    送给大一学弟学妹的几句话
    网络流小结
    后缀数组小结
    hdu5353
    UVALive 5792 Diccionário Portuñol
    概率dp小结
  • 原文地址:https://www.cnblogs.com/AK-ls/p/11787241.html
Copyright © 2011-2022 走看看