P1308 统计单词数
题目描述
一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数。
现在,请你编程实现这一功能,具体要求是:给定一个单词,请你输出它在给定的文章中出现的次数和第一次出现的位置。注意:匹配单词时,不区分大小写,但要求完全匹配,即给定单词必须与文章中的某一独立单词在不区分大小写的情况下完全相同(参见样例1 ),如果给定单词仅是文章中某一单词的一部分则不算匹配(参见样例2 )。
输入格式
共22行。
第1行为一个字符串,其中只含字母,表示给定单词;
第2行为一个字符串,其中只可能包含字母和空格,表示给定的文章。
输出格式
一行,如果在文章中找到给定单词则输出两个整数,两个整数之间用一个空格隔开,分别是单词在文章中出现的次数和第一次出现的位置(即在文章中第一次出现时,单词首字母在文章中的位置,位置从0 开始);如果单词在文章中没有出现,则直接输出一个整数-1。
输入输出样例
输入
To
to be or not to be is a question
输出
2 0
输入
to
Did the Ottoman Empire lose its power at that time
输出
-1
说明/提示
数据范围
1≤单词长度≤10。
1≤文章长度≤1,000,000。
错误代码:
起始想法是用stringstream流将字符串分割,然后逐个与单词比较,但是没看到单词子串不算觉得太繁琐放弃,最后参考了大佬的自动机、和利用空格和find查找,自己仿造着写了
正确代码
#include <iostream>
#include <string>
#include <algorithm>
const int SPACE = 0;
const int WORD = 1;//查找到是指定单词
const int LETTER = -1;//是字母但不是指定的字母
using namespace std;
string ToLower(string& s)
{
for (int i = 0; i < s.size(); i++)
{
if (isupper(s[i]))
{
s[i] = tolower(s[i]);
}
}
return s;
}
int main() {
string word;
getline(cin,word);
ToLower(word);
// cin.sync();//在vs上没用 ,并且最好不要在OJ上用这个
string a;
getline(cin, a);
ToLower(a);
// cout<<a;
int len = word.size();
int state = 0;//从空格状态开始
int sum = 0;//标记要查找的单词的总数
// int lsum = 0;//表明单词数目
int flag = -1;//标记是否为第一次进入
a += ' ';
for (int i = 0; i < a.size(); i++)
{
switch (state)
{
//表示前一个字符是空格
case SPACE:
if (a[i] == word[0])
{
state = 1;//没有必要再声明一个变量标记核对字符的数目
}
else if (a[i] == ' ')
{
}
else {
//如果不是核对单词也不是空格,那么只能是不对的单词
state = LETTER; //转换状态
}
break;
case LETTER:
if (a[i] == ' ')
{
//表明非核对单词已经结束
// lsum++;
state = SPACE;//转变状态
}//因为在非核对单词中的核对单词不是要找的单词,所以不考虑是核对单词的情况
break;
default://不是非查找单词就是空格和查找单词
if (state == len)
{//表示是上个字符最后一个字符
if (a[i] == ' ')//表明就是要找的字符
{
sum++;//符合情况的单词+1
state = SPACE;
// lsum++;
if (flag == -1)
{
flag = i - len;
}
}
else {
state = LETTER;
}
}
else {
if (a[i] == word[state])
{
state++;
}
else if (a[i] == ' ')//如果查找字符为aw a字符为a wd wd这样答案就错误
{
//表明到单词末尾了
state = SPACE;
// lsum++;
}
else {
//表示只是前面符合
state = LETTER;
}
}
break;
}
}
//lsum++;
if (flag == -1)
{
cout << "-1";
}
else {
cout << sum << " " << flag;
}
}
自动机的代码要注意的点是
- 要匹配单词到state与len相等,并且匹配到后面空格才说明是独立且完全匹配的单词,否则会有单词是文章单词子串的情况
- 这里本质是有三个状态,一个匹配单词、一个其他单词,另一个是空格,所以可以利用状态转换求解,但我对自动状态机不太熟,就不详细记录
另一个是利用find函数
#include <iostream>
#include <string>
using namespace std;
void ToLower(string& s)
{
for (int i = 0; i < s.size(); i++)
{
if (isupper(s[i]))
{
s[i] = tolower(s[i]);
}
}
// return s;
}
int main()
{
string index;
getline(cin,index);
// getchar();
string a;
getline(cin, a);
int pos = -1;
int num = 0;
ToLower(a);
ToLower(index);
a = ' ' + a + ' ';
index = ' ' + index + ' ';
while ((pos = a.find(index, pos + 1)) != -1)//外层括号必加,否则优先级使pos不会改变(pos = a.find(index, pos + 1))
{
num++;
}
if (num == 0)
{
cout << "-1";
}
else {
cout << num << " " << a.find(index);
}
return 0;
}
- find是属于我已知的函数了,但还是不会用,一是没注意题目独立单词才算,而是实在没想到空格的骚操作,但这里注意子串加空格而母串也要加空格才能保证find找到位置是与没加空格相同
- 子串加空格避免了单词是文章单词的子串问题
- C语言的大小写转换是一个字符一个字符的转换,目前先利用C语言的大小写转换改变大小写
- 这里还有一个注意的点,两个getline中间不需要getchar说明不会接收换行符
P1553 数字反转(升级版)
题目点这
正确代码:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()/*13点卡了*/
{
string s;
cin>>s;
if(s.find('.')!=-1)
{
string::iterator it = find(s.begin(),s.end(),'.');
reverse(s.begin(),it);
reverse(it+1,s.end());
while(s[0]=='0')
{
s.erase(s.begin());
}
while(*(s.end()-1)=='0')
{
s.erase(s.end()-1);
}
if(*(s.end()-1)=='.')
{
s+='0';
}
if(*(s.begin())=='.')
{
s.insert(s.begin(),'0');
}
}
else if(s.find('/')!=-1)
{
string::iterator it = find(s.begin(),s.end(),'/');
reverse(s.begin(),it);
reverse(it+1,s.end());
while(s[0]=='0')
{
s.erase(s.begin());
}
// while(*(s.end()-1)=='0')
// {
// s.erase(s.end()-1);
// }
if(*(s.begin())=='/')
{
s.insert(s.begin(),'0');
}
//字符串通过erase改变了'/'元素地址 ,所以迭代器失效了
it = find(s.begin(),s.end(),'/');
while(*(it+1)=='0')
{
s.erase(it+1);
}
}
else if(s.find('%')!=-1)
{
string::iterator it = find(s.begin(),s.end(),'%');
reverse(s.begin(),it);
while(s[0]=='0')
{
s.erase(s.begin());
}
if(*(s.begin())=='%')
{
s.insert(s.begin(),'0');
}
}
else
{
reverse(s.begin(),s.end());
while(s[0]=='0')
{
s.erase(s.begin());
}
if(s.size()==0)
{
s.insert(s.begin(),'0');
}
}
cout<<s;
}
问题分析:
- 本题巨坑,看了大佬汇总的数据点才做对,这里问题不要说整数、分数、百分数前面的0和小数后面的末尾0已经分子就是0的数据点,太坑了我思路完全没这么周全
- 这里最最重要的是慎用erase,数组下标改变,迭代器也改变了!!!!
问题3
P1321 单词覆盖还原
题目走这
问题分析:
- 一开始我以为有几个字母就是有几个单词,但是还有boy、girl、bo、oy等单个字母连在一起算一个单词的情况,于是写了count_if的谓词完全错误
- 这里是一个单独的字母、两个连贯的字母、三个连贯的字母都是算一个单词,当时完全没思路,看了大佬代码发现完全符合**||的特性**,只有一个为真即为真
下面为正确代码:
#include <iostream>
#include <algorithm>
//......boyogirlyy......girl.......
using namespace std;
int main()
{
string s;
cin >> s;
int b = 0;
int g = 0;
for(int i=0;i<s.size()-2;i++)
{
if(s[i]=='b'||s[i+1]=='o'||s[i+2]=='y')
{
b++;
}
}
for(int i=0;i<s.size()-3;i++)
{
if(s[i]=='g'||s[i+1]=='i'||s[i+2]=='r'||s[i+3]=='l')
{
g++;
}
}
cout << b << endl;
// g += count_if(s.begin(), s.end(), ff());
cout << g;
}
用||就不用分开讨论bo、oy等连在一起的情况