E-是不是复读机
题目描述:
在复读纪元2140年,复读机(们)已经放弃了如下所示的低级复读方式:
“哟,小伙汁,想不到你也是个复读机”
“哟,小伙汁,想不到你也是个复读机”
而是进化出了一种新的复读技巧:语义复读,即能复读出字面上高度相似的句子(单词顺序不一定一样)。譬如,在发生以下对话时,我们可以认为,参与对话的两人中存在复读机:
“I am not a repeater”
“I am not a repeater too”
现在我就来教你如何辨认复读机.
- 提取出所有的单词,并统计每一句中的词频。
2.通过余弦相似度算法进行相似度分析。
我们知道,在二维向量中,假设a向量是(x1, y1),b向量是(x2, y2),那么可以将余弦定理改写成下面的形式:
将我们之前提取出的词频看成2个n维向量,可得到(1,1,1,1,1,0)和(1,1,1,1,1,1)两个向量,进行余弦计算,可得到
我们认为其相似度为0.912,当相似度不小于90%时,我们就认为对话的两人中,存在复读机。
输入描述
输入包括两行由数字(0-9)、英文字符(a-z、A-Z)和空格组成的字符串。其中单词之间通过空格分隔且不区分大小写,即我们认为“At”、“at”、“AT”、“aT”为同一个单词。每个字符串总长度不超过2000.
输出描述:
在一行中输出结果,如果存在复读机,则输出“Yes”,否则输出“No”,不包含双引号。
示例1
输入
I am not a reapter I am not a reapter too
输出
Yes
示例2
输入
Excuse me Can you tell me how much the shirt is Yes it is nine fifteen
输出
No
思路解析:
知识点复习:substr
/*
string s = “0123456789”;
string sub1 = s.substr(5);一个数字:表示从下标为5开始一直到结尾:
sub1 = “56789”
string sub2 = s.substr(5, 3);从下标为5开始截取长度为3位:
sub2 = “567”
*/
样例中两句话要分别记录每个单词出现的次数,所以可以想到用两个map<string,int>容器去储存单词的次数,并且用set集合(不重复且自动排序的容器)去储存出现过的单词。
最后不用输入原句,所以可以将两句话全部转为大写或小写形式,便于在map映射和set集合中单词的处理。
这道题精髓的地方应该是如何在一串字符串中用substr切割每个单词。
需要使用string中的find函数去找空格。
当字符串不为空时,找到第一个空格的下标p,然后用一个新命名的string s变量暂时储存空格前的内容,也就是字符串头到空格下标p之间的内容,用substr(0,p)操作截取一个单词并用set集合中的insert插入到容器,这个string变量的map映射值也将相应加1,再循环以上操作,直到字符串剩下最后一个单词时,再单独特判:s != “” && s != " "
代码:
#include<bits/stdc++.h> using namespace std; int main() { string s1, s2, s; getline(cin, s1); getline(cin, s2); set<string> st; map<string, int> mp1, mp2; int i; for (i = 0; i < s1.length(); i++) { if (s1[i] >= 'A' && s1[i] <= 'Z') s1[i] = tolower(s1[i]); } for (i = 0; i < s2.length(); i++) { if (s2[i] >= 'A' && s2[i] <= 'Z') s2[i] = tolower(s2[i]); } int p = s1.find(" "); while (p != string::npos) { s = s1.substr(0, p); st.insert(s); s1 = s1.substr(p+1); mp1[s] += 1; p = s1.find(" "); } if (s1 != "" && s1 != " ") { st.insert(s1); mp1[s1] += 1; } p = s2.find(" "); while (p != string::npos) { s = s2.substr(0, p); st.insert(s); s2 = s2.substr(p+1); mp2[s] += 1; p = s2.find(" "); } if (s2 != "" && s2 != " ") { st.insert(s2); mp2[s2] += 1; } int up = 0, down1=0, down2 = 0; for (set<string>::iterator it = st.begin(); it != st.end(); it++) { up += mp1[*it]*mp2[*it]; down1 += mp1[*it]*mp1[*it]; down2 += mp2[*it]*mp2[*it]; } double sum = up*1.0/(sqrt(down1)*sqrt(down2)*1.0); if (sum >= 0.9000) cout << "Yes"; else cout << "No" ; return 0; }