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——常用结构
嗯,参加这个比赛还是有挺大收益的(仅这道题就挺多)。
当然对于这两点的应用和理解,今后肯定会更频繁和深入,我还有很多学的东西啊。