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

题意

一个字符串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

hdu4821 String的更多相关文章

  1. (通俗易懂小白入门)字符串Hash+map判重——暴力且优雅

    字符串Hash 今天我们要讲解的是用于处理字符串匹配查重的一个算法,当我们处理一些问题如给出10000个字符串输出其中不同的个数,或者给一个长度100000的字符串,找出其中相同的字符串有多少个(这样 ...

  2. hdu 4821 字符串hash+map判重 String (长春市赛区I题)

    http://acm.hdu.edu.cn/showproblem.php?pid=4821 昨晚卡了非常久,開始TLE,然后优化了之后,由于几个地方变量写混.一直狂WA.搞得我昨晚都失眠了,,. 这 ...

  3. 透过WinDBG的视角看String

    摘要 : 最近在博客园里面看到有人在讨论 C# String的一些特性. 大部分情况下是从CODING的角度来讨论String. 本人觉得非常好奇, 在运行时态, String是如何与这些特性联系上的 ...

  4. JavaScript String对象

    本编主要介绍String 字符串对象. 目录 1. 介绍:阐述 String 对象的说明以及定义方式. 2. 实例属性:介绍 String 对象的实例属性: length. 3. 实例方法:介绍 St ...

  5. ElasticSearch 5学习(9)——映射和分析(string类型废弃)

    在ElasticSearch中,存入文档的内容类似于传统数据每个字段一样,都会有一个指定的属性,为了能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理成字符串值,Elasticsearc ...

  6. [C#] string 与 String,大 S 与小 S 之间没有什么不可言说的秘密

    string 与 String,大 S 与小 S 之间没有什么不可言说的秘密 目录 小写 string 与大写 String 声明与初始化 string string 的不可变性 正则 string ...

  7. js报错: Uncaught RangeError: Invalid string length

    在ajax请求后得到的json数据,遍历的时候chrome控制台报这个错误:Uncaught RangeError: Invalid string length,在stackoverflow查找答案时 ...

  8. c# 字符串连接使用“+”和string.format格式化两种方式

    参考文章:http://www.liangshunet.com/ca/201303/218815742.htm 字符串之间的连接常用的两种是:“+”连接.string.format格式化连接.Stri ...

  9. 【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

    之前以为BinaryWriter写string会严格按构造时指定的编码(不指定则是无BOM的UTF8)写入string的二进制,如下面的代码: //将字符串"a"写入流,再拿到流的 ...

随机推荐

  1. 201521123003《Java程序设计》第7周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 参考资料: XMind 2. 书面作业 Q1.ArrayList代码分析 1.1 解释ArrayList的contains源 ...

  2. 201521123059 《Java程序设计》第十一周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 1.实现线程有两种方法:实现Ruannable接口和继承Thread类: 2.使用线程的start()方法启动线程 ...

  3. 201521123078 《Java程序设计》第十三周学习总结

    1. 本周学习总结 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu.edu.cn,分析返回结果有何不同?为什么会有这样的不同? 查询Ip地址 ...

  4. 生成/etc/shadow文件中的密码

    shadow文件的格式就不说了.就说说它的第二列--密码列. 通常,passwd直接为用户指定密码就ok了.但在某些情况下,要为待创建的用户事先指定密码,还要求是加密后的密码,例如kickstart文 ...

  5. python日记_01 python实现6个人围成一圈,扔到第三个人出局,循环扔的问题。

    #!/usr/bin/python shoplist=['mango','apple','carrot','banana','oracle','python'] length = len(shopli ...

  6. Ansible系列(二):选项和常用模块

    html { font-family: sans-serif } body { margin: 0 } article,aside,details,figcaption,figure,footer,h ...

  7. rpm包管理

    库文件 linux上,库文件是非常重要的,因为很多的软件都不是将所有的自己在需要的函数库自己写好,而是将一部分自己软件特有的库文件自己写,通用的库文件全部动态链接到公共库上去,这样不仅节省空间,同时用 ...

  8. Http协议基本知识简介

    HTTP协议是指超文本传输协议,位于应用层,HTTP规定数据格式,然后用tcp进行传输. 请求响应模式:简单理解为客户端对服务器发起请求,服务器响应客户端. 主要特点 无连接:无连接的含义是限制每次连 ...

  9. Oracle_Sequence如何初始化开始值

    Sequence的start with 值如何确定才能保证生成的主键不会冲突??? 我的项目中最开始数据库表主键的生成策略是 increment,但由于后来采用了集群部署的方式,出现了主键冲突的问题. ...

  10. mysql5.6.24的安装与简单使用

    1, 下载绿色版Mysql5.6.24 http://dlsw.baidu.com/sw-search-sp/soft/ea/12585/mysql-5.6.24-win32.1432006610.z ...