zoukankan      html  css  js  c++  java
  • COGS 902 乐曲主题 题解 & hash入门贺

    【题意】

    给定一个长为n的序列,元素都是不超过88的正整数,求序列中主题的最大长度。

    所谓主题是指在序列中出现了至少两次并且不相交的子串。特别的,主题可以变调,也就是说如果一个子串全部加上或减去一个数后与另一个子串相同,那么也认为它们是相同的。

    对于100%的数据,n<=5000。

    【解法】

    所谓的变调不过是升降趋势相同,直接来一发差分就好。

    答案显然有单调性,长度越长主题越不容易出现,因此可以二分答案,每次只要查询长为ans的子串中是否存在相同且不相交的两个子串即可。

    查询子串是否相同可以用哈希实现,这样只要O(n)预处理一发h数组并递推幂取模,取hash值就果断O(1)了。

    关于hash的取模,可以直接用unsigned long long自动溢出取模(虽然这是最容易被卡的hash……)。

    贴个代码(也不知道出了什么事儿,莫名的折叠不了了,垃圾cnblogs):

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<map>
     5 #define ULL unsigned long long
     6 using namespace std;
     7 const int maxn=5010;
     8 const ULL T=173ull;
     9 void init();
    10 ULL hash(int,int);
    11 ULL h[maxn],pw[maxn],tmp;
    12 int n,a[maxn],L,R,M;
    13 bool ok;
    14 map<ULL,int>id;
    15 int main(){
    16 #define MINE
    17 #ifdef MINE
    18     freopen("theme.in","r",stdin);
    19     freopen("theme.out","w",stdout);
    20 #endif
    21     scanf("%d",&n);
    22     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    23     for(int i=n;i;i--)a[i]-=a[i-1];
    24     init();
    25     L=4;R=n;
    26     while(L<=R){
    27         M=(L+R)>>1;
    28         ok=false;
    29         for(int i=1;i+M-1<=n;i++){
    30             tmp=hash(i,M);
    31             if(id.count(tmp)){
    32                 if(id[tmp]<i-M){
    33                     ok=true;
    34                     break;
    35                 }
    36             }
    37             else id[tmp]=i;
    38         }
    39         if(ok)L=M+1;
    40         else R=M-1;
    41     }
    42     if(L<5)L=0;
    43     printf("%d",L);
    44 #ifndef MINE
    45     printf("
    -------------------------DONE-------------------------
    ");
    46     for(;;);
    47 #endif
    48     return 0;
    49 }
    50 inline void init(){
    51     for(int i=n;i;i--)h[i]=h[i+1]*T+(a[i]+1);
    52     pw[0]=1ull;
    53     for(int i=1;i<=n;i++)pw[i]=pw[i-1]*T;
    54 }
    55 inline ULL hash(int x,int l){return h[x]-h[x+l]*pw[l];}
    56 /*
    57 30
    58 25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18
    59 82 78 74 70 66 67 64 60 65 80
    60 Answer:
    61 9
    62 */

    【hash相关】

    虽说是抄的蓝书的hash……但不管怎么说自己算是会写hash了……好感动……

    如果字符串给定,询问的串都是它的子串,那么可以O(n)预处理,O(1)查询。

    具体来说,定义后缀hash函数为

    hi=Tn-i+1si+Tn-isi+1+……+sn,其中T代表字符集大小。

    那么利用秦九韶大法,有如下递推式:

    hi=hi+1*T+si

    然后就可以happy的O(n)推出所有h值啦。

    查询的时候,记以x开头,长为l的子串的hash值为hash(x,l),那么有

    hash(x,l)=Tlsx+Tl-1sx+1+……+sx+l-1

    由上面h函数的公式,可以推出

    hash(x,l)=hx-Tlhx+l

    然后就可以happy的O(1)查询啦。

    233333333
  • 相关阅读:
    光遇————墓土(补充)蜡烛收集
    光遇————雨林
    每日光遇日记
    光遇————墓土
    光遇————云野超级不详细的蜡烛收集
    光遇————晨岛超级详细的蜡烛收集
    高精度
    HDU 1002: A + B Problem II (大数加法)
    HDU 1018:Big Number (位数递推公式)
    D2. Remove the Substring (hard version) (KMP-next数组 ) ( Codeforces Round #579 (Div. 3) )
  • 原文地址:https://www.cnblogs.com/hzoier/p/5962171.html
Copyright © 2011-2022 走看看