持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
力扣——472. 连接词
472. 连接词
给你一个 不含重复 单词的字符串数组 words ,请你找出并返回 words 中的所有 连接词 。
连接词 定义为:一个完全由给定数组中的至少两个较短单词组成的字符串。
示例 1:
输入:words = ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"]
输出:["catsdogcats","dogcatsdog","ratcatdogcat"]
解释:"catsdogcats" 由 "cats", "dog" 和 "cats" 组成;
"dogcatsdog" 由 "dog", "cats" 和 "dog" 组成;
"ratcatdogcat" 由 "rat", "cat", "dog" 和 "cat" 组成。
问题解析
题目描述简单,但是写起来可不简单。
首先如题目所说连接词是由多个在数组里的单词组合成的,那么对于每个单词,我们可以设一个bool数组f,f[i]表示,这个单词前i个字符都是由数组里的单词组合成的。那么当f[单词长度]为true时,说明这个词是一个合成词,初始f[0]=true。
当一个单词b可以匹配另一个单词a的第i到i+b.size() 个位置时,有:f[i+b.size()]=f[i] ;
基础思路有了,现在我们要做的就是,对于每个单词a,我们遍历它的每个位置,如果f[i]为true,且有某个单词b能够匹配这个单词接下来的b.size()个字符,我们就把f[i+b.size()]也变成true,当f[a.size()]为true时,说明这是一个连接词,我们把他记录下来。
新的问题就是,一共最多会有1e4个单词,一个单词长度最多为30,加上判断某个单词是否能匹配该单词,时间复杂度明显超了,我们此时就要想办法进行优化。
对于枚举单词来说,如果某个单词b能匹配另一个单词a的第i个位置,则这个单词b的首字母肯定要和a[i]相等,那么我们可以根据首字母将他们全部分开来,这样在我们找可能匹配成功的单词时,只要根据首字母去找就可以了,避免了枚举没用的单词。
至于判断每个单词是否能匹配成功,我们可以采用字符串哈希的方法。字符串哈希具体不展开说了,主要就是它通过把一个字符串转化成一个base进制的数,然后判断两个字符串是否相同,只用看这两个数转化出的数是否一样。预处理O(m),每次对比可以达到O(1)。我们先把所有单词的哈希值记录下来,这样当我们匹配单词时,只要看两边的哈希值是否一样即可。
AC代码
bool cmp(string&a,string&b)
{
if(a.size()!=b.size())return a.size()<b.size();
return a<b;
}
class Solution {
public:
typedef unsigned long long ull;
bool f[10050][35];
int p=99971,base=13331;
vector<ull>hash[10050];
//获取单词l到r位置的哈希值
ull get_hash(vector<ull>&v,int l,int r,vector<ull>&bpow)
{
ull t=v[l-1]*bpow[r-l+1];
return (v[r]-t);
}
vector<string> findAllConcatenatedWordsInADict(vector<string>& words) {
int n=words.size();
sort(words.begin(),words.end(),cmp);
int m=words.back().size();
vector<ull>bpow(m+1,1);
//按照首字母记录单词
unordered_map<char,vector<int>>mymap;
for(int i=1;i<=m;i++)
{
bpow[i]=bpow[i-1]*base;
}
//将每个单词的哈希值都算出来
for(int i=0;i<n;i++)
{
int len=words[i].size();
ull res=0;
hash[i].push_back(0);
for(int j=1;j<=len;j++)
{
res=(res*base+words[i][j-1]);
hash[i].push_back(res);
}
mymap[words[i][0]].push_back(i);
}
int mn=words[0].size();
vector<string>cnt;
for(int i=2;i<n;i++)
{
int len=words[i].size();
f[i][0]=true;
for(int j=0;j<len;j++)
{
if(!f[i][j])continue;
for(auto&k:mymap[words[i][j]])
{
int ans=words[k].size();
if(ans>len-j||words[k]==words[i])break;
ull a=get_hash(hash[i],j+1,j+ans,bpow),b=get_hash(hash[k],1,ans,bpow);
if(a==b)f[i][j+ans]=true;
}
}
if(f[i][len])
cnt.push_back(words[i]);
}
return cnt;
}
};