• 【二分答案+智障的字符串hash】BZOJ2946-[Poi2000]公共串(Ranklist倒一达成!!!!!)【含hash知识点】


    【题目大意】

    给出几个由小写字母构成的单词,求它们最长的公共子串的长度。

    【字符串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,显然轻松过去..嗯就这样,完结。
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 #define mod 10000019
     7 #define p 20011
     8 using namespace std;
     9 typedef long long ll;
    10 const int MAXN=10;
    11 const int MAXLEN=2000+50;
    12 int n,minl;
    13 ll hash[MAXN][MAXLEN];
    14 char str[MAXLEN];
    15 int len[MAXN];
    16 int vis[mod+1],times[mod+1];
    17 
    18 int check(int x)
    19 { 
    20     memset(vis,0,sizeof(vis));
    21     memset(times,0,sizeof(times));
    22     ll mul=1;
    23     for (int i=1;i<=x;i++) mul=(mul*p)%mod; 
    24     for (int i=1;i<=n;i++)
    25         for (int j=x;j<=len[i];j++)
    26         {
    27             ll nowhash=(hash[i][j]-(hash[i][j-x]*mul)%mod+mod)%mod;
    28             if (vis[nowhash]!=i)
    29             {
    30                 times[nowhash]++;
    31                 vis[nowhash]=i;
    32                 if (times[nowhash]==n) return 1;
    33             }
    34         }
    35     return 0;
    36 }
    37 
    38 void init()
    39 {
    40     minl=0x7fffffff;
    41     scanf("%d",&n);
    42     for (int i=1;i<=n;i++)
    43     {
    44         scanf("%s",str);
    45         len[i]=strlen(str);
    46         if (len[i]<minl) minl=len[i];
    47         hash[i][1]=str[0]-'a'+1;
    48         
    49         for (int j=2;j<=len[i];j++)
    50             hash[i][j]=(hash[i][j-1]*p+(str[j-1]-'a'+1))%mod;
    51     }        
    52 }
    53 
    54 void search_ans()
    55 {
    56     int lb=0,ub=minl+1;
    57     while (lb<ub)
    58     {
    59         int mid=(lb+ub)>>1;
    60         if (check(mid)) lb=mid+1;else ub=mid;
    61     }
    62     printf("%d",lb-1);
    63 }
    64 
    65 int main()
    66 {
    67     init();
    68     search_ans();
    69     return 0;
    70 }
  • 相关阅读:
    groovy hello world
    windows下使用命令行给通过genymotion创建的虚拟机配制IP地址
    洛谷1781 宇宙总统 解题报告
    洛谷1042 乒乓球 解题报告
    洛谷1031 均分纸牌 解题报告
    洛谷1023 税收与补贴问题 解题报告
    洛谷1540 机器翻译 解题报告
    洛谷1017 进制转换 解题报告
    [SDOI2011] 染色(Luogu 2486)
    树链剖分详解
  • 原文地址:https://www.cnblogs.com/iiyiyi/p/5701770.html
Copyright © 2020-2023  润新知