题目大意:找出给定的字符串中出现次数最多的单词,如果有多个,输出首字符ASCII码最小的单词。这里的单词,是指只含数字和字母字符的字符串。单词之间以非字母和数字字符分开,忽略大小写的差异。
主要考察的是循环的写法,怎么写这个循环bug能最少。用 for循环遍历思考量较少,找到一个数字或字母字符后,持续向后遍历(while),直到不是数字或字母字符,或者达到了字符串的末尾,这样就得到了一个单词。
AC代码:
#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
bool isAlphanumerical(char &c)
{
if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')) return true;
if(c >= 'A' && c <= 'Z')
{
c = c - 'A' + 'a';
return true;
}
return false;
}
int main()
{
string str;
getline(cin, str);
map<string, int> wordToCount;
int maxCount = 0;
for (int i = 0; i < str.size(); ++i)
{
if(isAlphanumerical(str[i]))
{
string word;
word += str[i++];
while(isAlphanumerical(str[i]))
{
word += str[i++];
if(i == str.size()) break;
}
wordToCount[word]++;
if(wordToCount[word] > maxCount)
maxCount = wordToCount[word];
}
}
for(map<string, int>::iterator it = wordToCount.begin(); it != wordToCount.end(); it++)
{
if(it->second == maxCount)
{
printf("%s %d", it->first.c_str(), maxCount);
break;
}
}
return 0;
}
如果用状态机的写法,例如初始状态为0,在状态0的条件下找到第一个数字或字母字符后设置状态为1,并记录单词的开始位置start;在状态1的条件下找到第一个非数字或字母字符,设置状态为0,则单词的结束位置是前一个,这样就找到了一个单词。但这样写有一个麻烦,就是原字符串的最后一个字符如果也符合条件,那么也需要被记录 (测试点4),但不会进入状态1->0的判断,所以需要单独拿出来。写法不太简洁。
AC代码:
#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
bool isAlphanumerical(char &c)
{
if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')) return true;
if(c >= 'A' && c <= 'Z')
{
c = c - 'A' + 'a';
return true;
}
return false;
}
int main()
{
string str;
getline(cin, str);
map<string, int> wordToCount;
int maxCount = 0;
int state = 0, start = 0;
for (int i = 0; i < str.size(); ++i)
{
if(isAlphanumerical(str[i]) && state == 0)
{
start = i;
state = 1;
}
else if(!isAlphanumerical(str[i]) && state == 1)
{
string word = str.substr(start, i-start);
wordToCount[word]++;
if(wordToCount[word] > maxCount)
maxCount = wordToCount[word];
state = 0;
}
if(i == str.size() - 1)
{
string word = str.substr(start, i - start + 1);
wordToCount[word]++;
if(wordToCount[word] > maxCount)
maxCount = wordToCount[word];
}
}
for(map<string,int>::iterator it = wordToCount.begin(); it != wordToCount.end(); it++)
{
if(it->second == maxCount)
{
printf("%s %d", it->first.c_str(), maxCount);
break;
}
}
return 0;
}
优化使用循环的代码,从左向右遍历,记录每一个单词的起点和终点,利用内置的类型判断函数 isalnum()和大小写转换函数 tolower()可以少写几行代码:
#include <bits/stdc++.h>
using namespace std;
map<string, int> mp;
int main()
{
string str;
getline(cin, str);
int left = 0, right = left + 1;
while(left < str.size())
{
while(left < str.size() && !isalnum(str[left])) left++;
right = left;
while(right < str.size() && isalnum(str[right]))
{
str[right] = tolower(str[right]);
right++;
}
right--;
string word = str.substr(left, right - left + 1);
mp[word]++;
left = right + 1;
}
int maxCnt = 0;
string ret;
for(auto it = mp.begin(); it != mp.end(); it++)
{
if(it->second > maxCnt)
{
maxCnt = it->second;
ret = it->first;
}
}
cout << ret << " " << maxCnt;
return 0;
}