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

题意

一个字符串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. 201521123042 Java第一周学习总结

    1. 201521123042 <Java程序设计>第一周学习总结 a.用notepad++和eclipse编写Java程序 b.安装Java Q1.为什么java程序可以跨平台运行?执行 ...

  2. POJ--3172 Scales (DFS 大容量背包 C++)

    Scales Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3148   Accepted: 851 Description ...

  3. returned a response status of 403 OR 409

    当我们使用jersy把图片上传到我们的图片服务器中[tomcat],我们可能会有以下的错误: returned a response status of 403 OR 409 403和409我都遇到过 ...

  4. 小甲鱼:Python学习笔记001_变量_分支_数据类型_运算符等基础

    1.下载并安装Python 3.5.0 Python是一个跨平台语言,Python从3.0的版本的语法很多不兼容2版本,官网找到最新的版本并下载:http://www.python.org 2.IDL ...

  5. 详解go语言的array和slice 【二】

    上一篇已经讲解过,array和slice的一些基本用法,使用array和slice时需要注意的地方,特别是slice需要注意的地方比较多.上一篇的最后讲解到创建新的slice时使用第三个索引来限制sl ...

  6. 微信小程序--图片相关问题合辑

    图片上传相关文章 微信小程序多张图片上传功能 微信小程序开发(二)图片上传 微信小程序上传一或多张图片 微信小程序实现选择图片九宫格带预览 ETL:微信小程序之图片上传 微信小程序wx.preview ...

  7. Android 之异步任务(AsyncTask,Handler,Message,looper)

    AsyncTask: 3个类型(Params,Progress和Result),4个步骤(onPreExecute(),doInBackground(Params…),onProgressUpdate ...

  8. Python dict operation introduce

    字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中 ,格式如下所示: d = ...

  9. maven详解之结构

    maven 父子关系 父项目中打包方式必须是pom  如 <packaging>pom</packaging>,父项目中使用<modules><module& ...

  10. Spring Boot Document Part II(下)

    Part II. Getting started 11. 开发第一个Spirng Boot Application使用Spring Boot的关键特征开发一个基于JAVA Web的“Hello Wor ...