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

题意

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

    1.本周学习总结 1.1思维导图 2. 书面作业 本次作业题集集合 1.List中指定元素的删除(题目4-1) 1.1 实验总结 1.删除元素的时候从最后一个元素开始,避免删除元素后位置发生变化而导致 ...

  2. 201521123027 《JAVA程序设计》第二周学习总结

    1.本周学习总结 1.学习使用码云管理程序代码: 2.认识类型(整数.字节.浮点数.字符与布尔)与变量的使用: 3.学习运算符的基本使用以及类型转换的基本规则: 4.学习String类的一些使用方法: ...

  3. 201521123076《Java程序设计》第1周学习总结

    一. 本章学习总结 1.了解了JDK,JVM,JRE的相关内容 JVM(Java Virtual Machine): Java虚拟机,*.java原始码,经过编译程序翻译为.class位码.JVM正是 ...

  4. 201521123001《Java程序设计》第9周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集异常 常用异常 题目5-1 1.1 截图你的提交结果(出现学号) 1.2 自己以前 ...

  5. Mysql数据库文件、表、记录的增删改查

    一.数据库文件夹的的操作 create database db1 charset utf8; 增加db1文件夹 show databases ; 查看所有数据库 show create databas ...

  6. python读取外部文件

    >>> pd.read_excel('c://111.xlsx') 年度排名 历史排名 电影名称 总票房 总人次 总场次 上映年份 操作 0 1 1 美人鱼 NaN -- -- 20 ...

  7. Oracle单引号的用法-转义

    在ORACLE中,单引号有两个作用:  1:字符串是由单引号引用  2:转义. 单引号的使用是就近配对.而在单引号充当转义角色时相对不好理解 1.从第二个单引号开始被视为转义符,如果第二个单引号后面还 ...

  8. Configuration Extensions - 简化配置,让你配置支持变量

    在开发"RabbitCloud"项目时,使用配置文件发现会有很多重复值,所以我基于"Microsoft.Extensions.Configuration"写了一 ...

  9. FS BPM 业余研发(用户详细操作手册--单人串行/并行)之 深圳分公司技术部请假审批流程

    1.FS BPM 简介 BPM软件中BPM是英文字母缩写,大致有二个意思.第一.Business Process Management,即业务流程管理,是一套达成企业各种业 务环节整合的全面管理模式. ...

  10. Jquery Ajax type的4种类型

    Ajax type这个里面填什么呢?通常大家都会说get和post.那么还有2个是什么呢 $.ajax({ url: 'http://www.cnblogs.com/youku/', type: '' ...