NOIP学习小站
西安交通大学附属中学航天学校

字符串例题——统计字符串中某单词出现次数

问题:统计字符串\(str\)中单词\(word\)出现的次数。已知字符串中的单词用一个空格或者多个空格隔开。

输入格式:有2行,第1行是字符串\(str\),第2行是不带空格的单词\(word\)。

分析:使用string来存储字符串\(str\)和单词\(word\)。可以通过string的find方法查找子串的方式统计单词出现次数,例如字符串和单词内容如下:

nice to meet you and how are you thank you very much
you

初步想法是用find方法统计单词you在字符串nice to meet you and how are you thank you very much 出现次数:

#include<iostream>
#include<string>
using namespace std;
int main()
{
	int cnt = 0;
	string str,word;
    getline(cin,str);
    cin>>word;
    //查找单词word第一次出现的位置 
    size_t pos = str.find(word);
    while(pos != string::npos){   //查找到
    	cnt++;
    	//从pos+word.length()起继续查找单词word第一次出现的位置 
    	pos = str.find(word,pos+word.length());
	}
	cout<<cnt<<endl;
	return 0;
} 

但是考虑下面的情况:

nice to meet you and how are you thank you and your friends very much
you

因为your中也完整出现了单词you,也会被统计到。find方法是查找子串,如果字符串\(str\)中某个单词中完整出现了单词\(word\)(此外还有其他字符),用上面的方法也会认为单词\(word\)出现了一次导致统计结果出错!那么该如何解决问题呢?

其实我们可以用find方法统计字符串" "+str+" "中单词" "+word+" "出现的次数,这样处理就能有效避免上面的问题(注意体会并掌握这种处理方法):

#include<iostream>
#include<string>
using namespace std;
int main()
{
	int cnt = 0;
	string str,word;
    getline(cin,str);
    str = " "+str+" ";
    cin>>word;
    word = " "+word+" ";
    //查找单词word第一次出现的位置 
    size_t pos = str.find(word);
    while(pos != string::npos){
    	cnt++;
    	//从pos+word.length()起继续查找单词word第一次出现的位置 
    	pos = str.find(word,pos+word.length());
	}
	cout<<cnt<<endl;
	return 0;
} 

如果我们用字符数组存储字符串,那么该如何统计单词出现次数呢?其实我们使用前一小节例题中的方法将字符串中的每个单词找出来:

#include<iostream>
#include<cstring>
using namespace std;
char str[1010];
int main()
{
    char last = ' ';    //记录上一个字符,体会这里默认值设置成空格字符的用途
    fgets(str,sizeof(str),stdin);
    
    int n = strlen(str);	//n是字符串长度 
	int start = 0;	      //start记录单词首字符下标 
    
    //处理fgets输入字符串末尾可能额外附带的'\n' 
	if(n && str[n-1]=='\n') str[--n] = '\0';
	
    for(int i=0;i<=n;i++){   //注意这里的条件是i<=n,而不是一般遍历字符串的i<n
	 
        //如果上一个字符是空格、当前字符不是空格,则找到一个新单词
        if(last == ' ' && str[i] != ' '){
            start = i;       //记录找到的单词的首字符下标
        }else if(last != ' ' && (str[i] == ' ' || str[i] == '\0')){ //单词结束
        	//从start到i-1是一个单词
        	for(int j=start;j<i;j++) cout<<str[j];
        	cout<<endl;
		}
		
        //last更新为当前字符,在下一次循环中就成了“上一个字符”
        last = str[i];
    }
    return 0;
} 

那么只需要判断每次找出来的单词是不是\(word\)就能统计单词\(word\)的数量了:

#include<iostream>
#include<cstring>
using namespace std;
char str[1010],word[1010];
int main()
{
	int cnt = 0; 
    char last = ' ';    //记录上一个字符,体会这里默认值设置成空格字符的用途
    fgets(str,sizeof(str),stdin);
    cin>>word;
    
    int n = strlen(str);	//n是字符串长度 
	int start = 0;		  //start记录单词首字符下标 
    
    //处理fgets输入字符串末尾可能额外附带的'\n' 
	if(n && str[n-1]=='\n') str[--n] = '\0';
	
    for(int i=0;i<=n;i++){   //注意这里的条件是i<=n,而不是一般遍历字符串的i<n
	 
        //如果上一个字符是空格、当前字符不是空格,则找到一个新单词
        if(last == ' ' && str[i] != ' '){
            start = i;
        }else if(last != ' ' && (str[i] == ' ' || str[i] == '\0')){ //单词结束 
        	int j,k;
        	//从start到i-1是一个单词
        	//比较字符串str的start到i-1的字符与word的字符是否依次相等 
			for(j=start,k=0;j<i && word[k];j++,k++){
        		if(str[j] != word[k]) break;
        	}
        	//全部匹配相等,并且单词都匹配结束,那么匹配到单词word 
        	if(j==i && word[k]=='\0') cnt++;
		}
		
        //last更新为当前字符,在下一次循环中就成了“上一个字符”
        last = str[i];
    }
    cout<<cnt<<endl;
    return 0;
}