zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    【Anagrams】 cpp
    【Count and Say】cpp
    【Roman To Integer】cpp
    【Integer To Roman】cpp
    【Valid Number】cpp
    重构之 实体与引用 逻辑实体 逻辑存在的形式 可引用逻辑实体 不可引用逻辑实体 散弹式修改
    Maven项目聚合 jar包锁定 依赖传递 私服
    Oracle学习2 视图 索引 sql编程 游标 存储过程 存储函数 触发器
    mysql案例~tcpdump的使用
    tidb架构~本地化安装
  • 原文地址:https://www.cnblogs.com/acbingo/p/4719960.html
Copyright © 2011-2022 走看看