力扣:472. 连接词

2,474 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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;
    }
};