【二分答案+智障的字符串hash】BZOJ2946-[Poi2000]公共串(Ranklist倒一达成!!!!!)【含hash知识点】
2016-07-24 22:21
387 查看
【题目大意】
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
【字符串hash的小笔记】
hash[i]=(hash[i-1]*p+idx(s[i]))%mod,idx为映射值,一般a..z映射1..26;
习惯上,p取一个6到8位的素数即可,mod一般取大素数 1e9+7(1000000007)或1e9+9(1000000009)。
hash[i]=(hash[i-1]*p+idx(s[i]))%mod 表示第 i 个前缀的hash值,是一个hash的前缀和,那么,要求S[l…r]这个子串的hash值:
hash[l..r]=(hash[r]-hash[l-1]*(p^(r-l+1)))%mod(假设字符串下标从1开始)
【思路】
据说是后缀自动机,然而蒟蒻并不会后缀自动机,所以硬着头皮用hash搞一搞。上次立了个flag,什么本年度最丑代码,这个才是吧……脑洞产物,绝非正解,能否过全看rp。
首先圆滚滚地把每一个单词的hash值求一下,然后快乐地开始二分公共子串的长度。
毛茸茸地设了vis和times两个数组。times[i]表示当前hash值i已经出现在几个单词里。枚举每个单词中子串的开始位置,O(1)时间求该子串的hash,然后times加一。当然同一个长单词里面可能有相同的子串,所以用vis记录一下这个hash值最后出现在第几个单词中,来判断该子串在当前字符串中是不是第一次出现。
如果times加到n了,说明每个单词里面都有这个子串,这个解是正确的,哦耶搞定!
时间复杂度的话,求hash是O(|S|*n),|S|表示最长的子串长度。二分答案部分为(log|S|*n*|S|)。
所以总体的期望复杂度为O(log|S|*|S|*n),而n只有5,|S|只有2000,显然轻松过去..嗯就这样,完结。
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
【字符串hash的小笔记】
hash[i]=(hash[i-1]*p+idx(s[i]))%mod,idx为映射值,一般a..z映射1..26;
习惯上,p取一个6到8位的素数即可,mod一般取大素数 1e9+7(1000000007)或1e9+9(1000000009)。
hash[i]=(hash[i-1]*p+idx(s[i]))%mod 表示第 i 个前缀的hash值,是一个hash的前缀和,那么,要求S[l…r]这个子串的hash值:
hash[l..r]=(hash[r]-hash[l-1]*(p^(r-l+1)))%mod(假设字符串下标从1开始)
【思路】
据说是后缀自动机,然而蒟蒻并不会后缀自动机,所以硬着头皮用hash搞一搞。上次立了个flag,什么本年度最丑代码,这个才是吧……脑洞产物,绝非正解,能否过全看rp。
首先圆滚滚地把每一个单词的hash值求一下,然后快乐地开始二分公共子串的长度。
毛茸茸地设了vis和times两个数组。times[i]表示当前hash值i已经出现在几个单词里。枚举每个单词中子串的开始位置,O(1)时间求该子串的hash,然后times加一。当然同一个长单词里面可能有相同的子串,所以用vis记录一下这个hash值最后出现在第几个单词中,来判断该子串在当前字符串中是不是第一次出现。
如果times加到n了,说明每个单词里面都有这个子串,这个解是正确的,哦耶搞定!
时间复杂度的话,求hash是O(|S|*n),|S|表示最长的子串长度。二分答案部分为(log|S|*n*|S|)。
所以总体的期望复杂度为O(log|S|*|S|*n),而n只有5,|S|只有2000,显然轻松过去..嗯就这样,完结。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define mod 10000019 #define p 20011 using namespace std; typedef long long ll; const int MAXN=10; const int MAXLEN=2000+50; int n,minl; ll hash[MAXN][MAXLEN]; char str[MAXLEN]; int len[MAXN]; int vis[mod+1],times[mod+1]; int check(int x) { memset(vis,0,sizeof(vis)); memset(times,0,sizeof(times)); ll mul=1; for (int i=1;i<=x;i++) mul=(mul*p)%mod; for (int i=1;i<=n;i++) for (int j=x;j<=len[i];j++) { ll nowhash=(hash[i][j]-(hash[i][j-x]*mul)%mod+mod)%mod; if (vis[nowhash]!=i) { times[nowhash]++; vis[nowhash]=i; if (times[nowhash]==n) return 1; } } return 0; } void init() { minl=0x7fffffff; scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%s",str); len[i]=strlen(str); if (len[i]<minl) minl=len[i]; hash[i][1]=str[0]-'a'+1; for (int j=2;j<=len[i];j++) hash[i][j]=(hash[i][j-1]*p+(str[j-1]-'a'+1))%mod; } } void search_ans() { int lb=0,ub=minl+1; while (lb<ub) { int mid=(lb+ub)>>1; if (check(mid)) lb=mid+1;else ub=mid; } printf("%d",lb-1); } int main() { init(); search_ans(); return 0; }
相关文章推荐
- Android源代码编译完成之后运行模拟器
- 《原型图之选项卡》
- [BZOJ1189][HNOI2007]紧急疏散evacuate(二分+网络流)
- Java注解初步了解
- UVA 1599 Ideal Path 【两次BFS+贪心】 (好题)
- UVA 1599 Ideal Path 【两次BFS+贪心】 (好题)
- U-boot官方2013.10版本移植流程
- 线段树总结
- 32位Ubuntu 12.04下J-Link v8的配置及使用以及问题详解
- ART世界探险(5) - 计算指令
- DW——CSS——3D环境
- RGB游戏解析
- 【杭电2002】计算球体积
- 以太坊平台评估 私有链和联盟链的机会与挑战
- 定时器的合并和变速
- Android闹钟设置的解决方案
- 単例模式
- Java 使用对话框选择文件并输出到控制台
- iOS NSMapTable
- servlet生命周期