• hdu4821 String


    您也可以在我的个人博客中阅读此文章:跳转

    题意

    一个字符串S 问其中有几个子串能满足以下条件:
    1、长度为m*l
    2、可以被分成m个l长的不同的子串
    问题就变成了如何快速的判断着m个子串是否存在相同的

    思路与分析

    这题我做了两天,用了四种不同的方法,从TL到WA再到AC。。。一路坎坷。
    不过收获了很多东西,这题价值很大,堪称经典。
    运用滑动窗口的思想,长度为m*l的为大窗,将其划分为m个长度为l的小窗。
    枚举大窗的起点,以l为单位滑动大窗
    直接将所有的子串存储起来会MLE,所以想到hash
    将总串s hash,然后想办法快速的取出hash[s[i]~s[j]]
    最终算法如下:

    1. 大窗起点
    2. 计算当前大窗口下m个小窗的hash值,扔到map里
    3. 对map里的元素个数进行统计,若为m,ans++
    4. 以l为单位滑动大窗,每滑动一次,map减掉一个出窗口的l串,加上一个刚刚进窗口的l串,若此时map.size()==m,ans++
    5. 大窗起点+1,返回1

      如何快速计算hash[s[i]~s[j]]

      方法1

      这里给出某大神的方法:
    6. base为基数,从S最后一个字符开始,hash[i]=hash[i+1]*base+str[i]-‘a’+1,即将i位以后的串hash成一个unsigned long long
    7. hash[s[i]~s[j]]的值即为: hash[i]-hash[j+1]*nbase[len].nbase[i]表示base的i次方
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      struct HashTab{
      ULL Has[1000005];
      ULL base=31;
      ULL nbase[1000005];
      void init(char *s){
      int len=strlen(s);
      for (int i=len-1;i>=0;i--) {
      Has[i]=Has[i+1]*base+s[i]-'a'+1;
      }
      nbase[0]=1;
      for (int i=1;i<=len;i++)
      nbase[i]=nbase[i-1]*base;
      return;
      }
      ULL getHash(LL l,LL r){//hashtab.getHash(l,r)表示返回s[l~r]的hash值
      int len=r-l+1;
      return Has[l]-Has[l+len]*nbase[len];
      }
      };

    方法2

    没看懂,直接给出函数吧

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    struct Hash{
    LL B,mod,len,Has[1000005],Base[1000005];
    void init(char *s,LL _B,LL _mod){
    len=strlen(s);
    B=_B; mod=_mod;
    Base[0]=1; Has[0]=0;
    for (LL i=1;i<=len;i++){
    Base[i]=Base[i-1]*B%mod;
    Has[i]=(Has[i-1]*B+s[i-1]-'a'+1)%mod;
    }
    return;
    }
    LL getHash(LL l,LL r){ //hashtab.getHash(l,r)表示返回s[l~r]的hash值
    l++;r++;
    return ((Has[r]-Has[l-1]*Base[r-l+1])%mod+mod)%mod;
    }
    };

    主代码

    写的时候要注意下表是否正确哇,边界处到底有没有=号等等。。不然debug的时候是真头疼

    方法1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    #include <iostream>
    #include <map>
    #include <cstring>
    #include <cstdio>
    #define ULL unsigned long long
    #define LL long long
    using namespace std;
    map <ULL,int> mp;
    struct HashTab{
    ULL Has[1000005];
    ULL base=31;
    ULL nbase[1000005];
    void init(char *s){
    int len=strlen(s);
    for (int i=len-1;i>=0;i--) {
    Has[i]=Has[i+1]*base+s[i]-'a'+1;
    }
    nbase[0]=1;
    for (int i=1;i<=len;i++)
    nbase[i]=nbase[i-1]*base;
    return;
    }
    ULL getHash(LL l,LL r){
    int len=r-l+1;
    return Has[l]-Has[l+len]*nbase[len];
    }
    };
    HashTab _hash;
    int main () {
    int m,l;
    char s[100010];
    while (scanf("%d%d",&m,&l)!=EOF){
    scanf("%s",s);
    _hash.init(s);
    int len=strlen(s);
    int ans=0;
    for (int i=0;i<l&&i+m*l-1<len;i++){
    mp.clear();
    for (int j=i;j<i+m*l;j+=l){//计算小窗
    int tmp=_hash.getHash(j,j+l-1);
    //cout<<j<<" "<<tmp<<endl;
    mp[tmp]++;
    }
    if (mp.size()==m) ans++;
    for (int j=i+m*l;j+l<=len;j+=l){//以l为单位滑动大窗口
    int tmp=_hash.getHash(j-m*l,j-(m-1)*l-1);
    //cout<<"-"<<j-m*l<<" "<<tmp<<endl;
    mp[tmp]--;
    if (mp[tmp]==0) mp.erase(tmp);
    tmp=_hash.getHash(j,j+l-1);
    //cout<<"+"<<j<<" "<<tmp<<endl;
    mp[tmp]++;
    if (mp.size()==m) ans++;
    }
    }
    cout<<ans<<endl;
    }
    return 0;
    }

    方法2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    #include <iostream>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <map>
    #define MOD 1000000007
    #define LL long long
    using namespace std;
    map <long long,int> mp;
    struct Hash{
    LL B,mod,len,Has[1000005],Base[1000005];
    void init(char *s,LL _B,LL _mod){
    len=strlen(s);
    B=_B; mod=_mod;
    Base[0]=1; Has[0]=0;
    for (LL i=1;i<=len;i++){
    Base[i]=Base[i-1]*B%mod;
    Has[i]=(Has[i-1]*B+s[i-1]-'a'+1)%mod;
    }
    return;
    }
    LL getHash(LL l,LL r){
    l++;r++;
    return ((Has[r]-Has[l-1]*Base[r-l+1])%mod+mod)%mod;
    }
    };

    Hash _hash;
    int main () {
    int m,l;
    char s[100010];
    while (scanf("%d%d",&m,&l)!=EOF){
    scanf("%s",s);
    _hash.init(s,131,MOD);
    int len=strlen(s);
    int ans=0;
    for (int i=0;i<l&&i+m*l-1<len;i++){
    mp.clear();
    for (int j=i;j<i+m*l;j+=l){//计算小窗
    int tmp=_hash.getHash(j,j+l-1);
    //cout<<j<<" "<<tmp<<endl;
    mp[tmp]++;
    }
    if (mp.size()==m) ans++;
    for (int j=i+m*l;j+l<=len;j+=l){//以l为单位滑动大窗口
    int tmp=_hash.getHash(j-m*l,j-(m-1)*l-1);
    //cout<<"-"<<j-m*l<<" "<<tmp<<endl;
    mp[tmp]--;
    if (mp[tmp]==0) mp.erase(tmp);
    tmp=_hash.getHash(j,j+l-1);
    //cout<<"+"<<j<<" "<<tmp<<endl;
    mp[tmp]++;
    if (mp.size()==m) ans++;
    }
    }
    cout<<ans<<endl;
    }
    return 0;
    }

    参考

    1. 高神 http://www.acmtime.com/?p=579
    2. http://blog.csdn.net/houserabbit/article/details/25740235
  • 相关阅读:
    STL--sort源码分析
    进程和线程的区别
    static 关键字 静态成员变量及静态成员函数
    二叉树遍历总结 先序、中序、后续、广度、深度
    C++用new和不用new创建类对象区别
    传输层--TCP和UDP的区别
    传输层的作用
    微信商户/H5支付申请 被拒原因:网站存在不实内容或不安全信息
    Oracle本地网络服务名配置
    存储过程常用技巧
  • 原文地址:https://www.cnblogs.com/acbingo/p/4719960.html
Copyright © 2020-2023  润新知