题面
思路
一种暴力的做法是枚举词典内单词可以组成的所有复合词,判断组成的复合词是否在词典内。时间复杂度O(n^2),显然会TLE。
不妨先将词典内的单词按字典序排序(本题中输入已经满足字典序排序),依次枚举词典中的单词,往后查找是否有以该单词为前缀的单词,如果有这样的单词,再用二分查找判断该单词的后缀是否存在于词典当中,如果是,则该单词为复合词。
要点:
只需要向后查找是因为按照字典序排列后,以一个单词为前缀的单词一定在该单词之后。
向后查找时,如果遇到了一个不以该单词为前缀的单词,则可以退出查找,因为在字典序下此后不会再有满足条件的单词了,可以大幅节省时间。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
using namespace std;
string dict[120010];//储存单词
bool compound[120010];
int main(){
int n=0;
while(cin>>dict[n]){
n++;
}
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
int l=dict[i].length();string s=dict[j].substr(0,l);
if(dict[i]==s){//i是j的前缀,则查找j的后缀是否在字典中
string sub=dict[j].substr(dict[i].length(),dict[j].length()-1);
if(binary_search(dict,dict+n,sub)){
compound[j]=true;
}
}
else break;//不是前缀,说明此后单词均不以i为前缀,直接切出
}
}
for(int i=0;i<n;i++){
if(compound[i]) cout<<dict[i]<<endl;
}
return 0;
}
知识点
字符串的截取/删除
删除一部分字符可以理解为取出另一部分字符,使用substr,取出子串。
substr第一个参数代表从第几个字符开始,第二个代表取几个字符。
substr不会改变原字符串的值,他是返回了一个新的字符串。
例如下面的程序取出了字符串的前3个字符。
#include<string>
#include<iostream>
int main(){
std::string s = "helloworld";
std::string sub = s.substr(0,3);
std::cout << sub << std::endl;
return 0;
}
二分查找
binary_search(startaddress, endaddress, valuetofind)
startaddress: 列表起始地址
endaddress: 列表末尾地址
valuetofind: 查找的目标值