【题目链接】
OpenJudge NOI 1.13 26:n-gram串频统计
【题目考点】
1. 字符串
2. 顺序查找
3. STL map
【解题思路】
解法1:数组中顺序查找
设string类对象数组w,w[i]
表示第i个子串。设计数数组ct,ct[i]
表示第i个子串出现的个数。
遍历输出的字符串,取出每个长为n的子串t。在数组w中顺序查找是否存在t:
- 如果存在t,那么该子串出现的次数加1。
- 如果不存在t,那么在w数组中添加子串t,出现的次数为1。
在构造好w与ct后,求ct中的最大值mx。mx即为子串出现的最高频度。
如果mx小于等于1,就要输出NO。否则,遍历ct,输出其中频度等于mx的子串。
解法2:使用STL map
map可以用来保存键值对,本题中键是子串,值是子串出现的频度。
先枚举原字符串中所有长为n的子串,统计出每个子串出现的频度,保存在map<string, int> mp
之中,mp[sub]
就是子串sub出现的频度。
而后遍历mp,找到子串出现的最大频度mx。
如果mx小于等于1,输出NO。
否则从前向后枚举所有原字符串的长为n的子串,如果该子串的频度为mx,则输出该子串,而后把该子串的频度改为0,使得下一次枚举到该子串时不输出该子串。
【题解代码】
解法1:数组中顺序查找
#include<bits/stdc++.h>
using namespace std;
#define N 505
string s, w[N], t;//w[i]:第i个子串
int n, wi, ct[N], mx;//ct[i]:第i个子串出现的次数 mx:最高频度
int main()
{
cin >> n >> s;
for(int i = 0; i <= s.length()-n; ++i)//最后一次取到的子串下标为len-n~len-1
{
t = s.substr(i, n);//取到的子串
bool isfound = false;
for(int j = 1; j <= wi; ++j)//顺序查找
{
if(w[j] == t)
{
ct[j]++;
isfound = true;
break;
}
}
if(isfound == false)//如果没找到,则添加一个子串
{
w[++wi] = t;
ct[wi] = 1;
}
}
for(int i = 1; i <= wi; ++i)//求最大的频度
mx = max(mx, ct[i]);
if(mx <= 1)
cout << "NO";
else
{
cout << mx << endl;//输出最高频度
for(int i = 1; i <= wi; ++i)
{
if(ct[i] == mx)//输出频度最高的子串
cout << w[i] << endl;
}
}
return 0;
}
解法2:使用STL map
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n, mx = 0;//最高频度
map<string, int> mp;//mp[子串]:子串个数
string s, sub;
cin >> n >> s;
for(int i = 0; i <= s.length()-n; ++i)
{
sub = s.substr(i, n);//子串
mp[sub]++;//如果不存在键为sub的键值对,会先插入键值对(sub,0),而后让值增加1。
}
for(pair<string, int> p : mp)
mx = max(mx, p.second);//求所有键值对中值(子串个数)的最大值
if(mx <= 1)
cout << "NO";
else
{
cout << mx << endl;
for(int i = 0; i <= s.length()-n; ++i)
{
sub = s.substr(i, n);//子串
if(mp[sub] == mx)//如果子串出现的频度为最大频度
{
cout << sub << endl;
mp[sub] = 0;//每种子串只输出一次,如果再次取到sub,则不会输出。
}
}
}
return 0;
}