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

题意

一个字符串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. 学号:201521123116 《java程序设计》第四周学习总结

    1. 本周学习总结 2. 书面作业 Q1. 注释的应用使用类的注释与方法的注释为前面编写的类与方法进行注释,并在Eclipse中查看.(截图)答:注释的插入:注释以/开始,以/结束类注释/**.... ...

  2. 201521123032 《Java程序设计》第2周学习总结

    1. 本周学习总结 本周java回顾了各种数据类型,在java中使用浮点型会不精确,改用double行就好.学习了string的类型,string的对象是不可变的,创建之后不能再修改,在string的 ...

  3. 201521123057 《Java程序设计》第12周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 1.字符流与文本文件:使用 PrintWriter(写),BufferedReader(读) ...

  4. JAVA课程设计+五子棋(团队博客)

    JAVA课程设计 利用所学习的JAVA知识设计一个五子棋小游戏 1.团队名称.团队成员介绍(菜鸟三人组) 杨泽斌[组长]:201521123049 网络1512 叶文柠[组员]:20152112305 ...

  5. 201521123053《Java程序设计》第十周学习总结

    ---恢复内容开始--- 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 在java多线程程序中,所有线程都不允许抛出未捕获的checked excepti ...

  6. JavaEE error整理(不断更新)

    该文章用于整理开发中遇到的一些错误,及解决方法,不断整理更新. 1. 缺包异常 异常1:java.lang.NoClassDefFoundError: org/apache/commons/loggi ...

  7. WebUtils复用代码【request2Bean、UUID】

    request封装到Bean对象 public static <T> T request2Bean(HttpServletRequest httpServletRequest, Class ...

  8. 《Head First Java》读书笔记(3) - 异常和IO

    1.异常处理 我们在调用某个方法时,会被编译器告知需要捕捉异常和处理,意味着你调用的这个方法是有风险的,可能会在运行期间出状况,你必须写出在发生状况时加以处理的代码,未雨绸缪!这就是Java中异常处理 ...

  9. js中判断undefined类型

    typeof 运算符返回一个用来表示表达式的数据类型的字符串.可能的字符串有:"number"."string"."boolean".&qu ...

  10. springmvc05-Spring+Springmvc+Hibernate实现简单的用户管理系统

    1, 导入\spring-framework-3.2.4.RELEASE\libs\disk下所有包; hibernate-distribution-3.6.7.Final\lib\required下 ...