STL中map的简单使用&常数优化

本地某大学校赛某题[flower]的总结——常数优化&Map的浅略使用

关于这道题,是我赛场上比较遗憾的(~~我连题都没读懂~~)
把题面抄下来先

### The flower
Every problem maker has a flower in their heart out of love for ACM. As a famous problem maker, hery also has a flower.
Hery define string T as flower−string if T appears more than twice in string S and |T|=k. Hery wants you to find how many flower−string in S.

输入格式
The first line contains a string S consist of ′f′,′l′,′o′,′w′,′e′,′r′ and an integer k.
(1≤|S|≤1e5,1≤k≤100)

输出格式

 


 

Output an integer m in the first line, the number of flower−string in S.
Next m lines, each line contains a flower−string and the lexicographical order of them should be from small to large

 


 

样例
**input**

 


 

flowerflowerflower 3

 


 

**output**

 


 

4
flo
low
owe
wer

 



大致的翻译就是每只出题人都在心里种着一颗花(???)
定义一个flower-string ,由“f,l,o,w,e,r”组成,规定其长度为k,
在S中,问存在多少个长度为k的flower-string。
然后只有出现过两次以上的flower-string 才能算做flower-string,输出时要按字典序。

因为这个样例太特殊了,正好都是flower,所以我就以为是在flower中找到长度为k的string,而且以为只要结尾是wer就好,因此我就想,他最多也就6个,因为即使是从f开始,长度为7的串,进行六次操作,也会回到wer(相当于接龙接到最后,几个串里的字母去重后就是flower)。

然而,刚刚我的翻译也能看的出来,这题根本不是这个意思。

所以这个题什么意思呢?就是找在一个长串中,有多少个出现2次以上的(不包括二次)长度为k的字串。

于是,就有一个O(N)的想法,就是从长串的每一个字母打头,做一个复杂为k的循环,这样最大复杂度就是k*N,也就是1e7,

所以这里要用到map!

 

------------
为什么要用到map?因为要统计一下,某一个字串出现的次数,那么假如说发现了一个长度为k的字串,如何统计它的出现次数,保证它出现了三次?(三次以上出现的话就要进行去重而不是记录的操作了)。
我所知道的是,对于一个数,判断其是否出现过,可以用bool数组(素数筛用到的)
也可以用一个int数组,让它的值保证为3再记录

而这里要进行标记的[键],(他们都是这么叫的),是一个string。因此无法用下标类型为int的容器来操作。

### MAP

经过学习了解,我对map有一个理解。

map<key_type,value_type>x;
//定义了一个以key_type作键的,value_type作值的"容器"

那么在这道题中,需要的是一个map<string,int>h;

我的通俗(浅显)理解是,我这里的map定义了一个“桶”,这个桶上面的标号不是1,两,3,4,5......而是类似a22,bkk,cmm,d45一类的字符串;

所以说,我所理解的map定义容器的逻辑是:map<“桶”所用名字的类型,“桶”里装的东西>;
那么也就近似的认为,数组就是map<int,int>h;然而map不需要你进行范围的设置,也就是说你不用管它里面要存多少(当然存的多肯定会爆)

举个例子,int a[1000],规定了下标只能是0~999,数组里面存的数可以是int类型,而map<int,int>里面则没有规定下标,你在记录一个值的时候,直接将这个值的下标(可以是任意的你想要的在int范围内的下标)给作为键,再将这个值存进去。

所以这道题可以用map<string,int>h;从S中枚举所有长度为k的string,然后这些串都作为键,用value来记录出现的次数。

因此这个程序的核心就出来了。


------------

 

#include<bits/stdc++.h>
using namespace std;
string k[101000];
map<string,int>m;
int main()
{
string a;
cin>>a;
int n;
cin>>n;
int sum=0;
for(int i=0;i<a.size()-n+1;i++)//从每一个字符做n次枚举
{    
string b;//中转string
for(int j=i;j<=i+n-1;j++)//枚举
{
b+=a[j];//把k个char一个个塞到这个string里
}
m[b]++;//让名为b的桶里的值加一,代表其出现一次
if(m[b]==3) //等到这个桶里的值达到三,则符合要求,增加最终结果数量的值,并将此string存到结果串里。
{
sum++;
k[sum]=b;
}
}
cout<<sum<<endl;
sort(k+1,k+sum+1);
for(int i=1;i<=sum;i++)
{
cout<<k[i];
printf("\n");//对于string的输出唯一能做的时间优化,大概就是格式化换行了。
}
return 0;
}

 


------------

当我觉得我终于要用map解题的时候,我发现我tle了(我太弱了)
于是我再想怎么优化,然后我首先想到了氧气。

#pragma GCC optimize(2)//开个氧气优化

 



结果我把氧气放进去的时候,依然超时。

我就很奇怪,然而这个时候要就寝了,于是就只好暂停...

于是时间来到了第二天。

我一大早吃完饭就迅速跑向机房(~~这个好像很废话~~)

然后当我这个蒟蒻百思不得其解的时候,来了一位机房大佬,一眼就看出了我超时的原因,在于

for(int i=0;i<a.size()-n+1;i++)

 



他提醒我这个计算串长度的操作在每一次循环都会做一次!
(蒟蒻恍然大悟)
于是我才想到了常常被我忽略的常数优化。

而据说stl中.size()操作是o(n)的。
于是n*n就很明显会爆了。

那么就要讲我在这道题学到的另一个教训了。
### 常数优化

其实就是对于一个要多次调用的常数预处理一下,然后就可以减少时间。

比如这道题

 

for(int i=0;i<a.size()-n+1;i++)

 


然后我再改一下循环里的这一点,成为p,就可以见效了。

### 小结

常数优化——常用技巧

map——常用结构

嗯,参加这个比赛还是有挺大收益的(仅这道题就挺多)。

当然对于这两点的应用和理解,今后肯定会更频繁和深入,我还有很多学的东西啊。

posted @ 2019-12-25 21:30  explorerxx  阅读(743)  评论(0编辑  收藏  举报