匹配子序列的单词数

给定字符串 S 和单词字典 words, 求 words[i] 中是 S 的子序列的单词个数。

示例:
输入:
S = “abcde”
words = [“a”, “bb”, “acd”, “ace”]
输出: 3
解释: 有三个是 S 的子序列的单词: “a”, “acd”, “ace”。

注意:

所有在words和 S 里的单词都只由小写字母组成。
S 的长度在 [1, 50000]。
words 的长度在 [1, 5000]。
words[i]的长度在[1, 50]。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-matching-subsequences
因为 S 很长,所以寻找一种只需遍历一次 S 的方法,避免暴力解法的多次遍历。

解题思路:将所有单词根据首字母不同放入不同的桶中。例如当 words = [‘dog’, ‘cat’, ‘cop’],根据首字母不同可以分为 ‘c’ : (‘cat’, ‘cop’), ‘d’ : (‘dog’,)。换句话说,每个桶中的单词就是该单词正在等待匹配的下一个字母。在遍历 S 的同时,将匹配到单词根据下一个需要匹配的字母移动到不同的桶中。

例如,有字符串 S = ‘dcaog’:

初始化 heads = 'c' : ('cat', 'cop'), 'd' : ('dog',);
遍历 S[0] = 'd' 后,heads = 'c' : ('cat', 'cop'), 'o' : ('og',);
遍历 S[1] = 'c' 后,heads = 'a' : ('at',), 'o' : ('og', 'op');
遍历 S[2] = 'a' 后,heads = 'o' : ('og', 'op'), 't': ('t',) ;
遍历 S[3] = 'o' 后,heads = 'g' : ('g',), 'p': ('p',), 't': ('t',);
遍历 S[0] = 'g' 后,heads = 'p': ('p',), 't': ('t',)。

算法

使用长度为 26 的数组 heads 做桶,每个字母对应一个桶。访问 S 中的每个字母时,将该字母对应桶中的所有单词,根据下一个等待匹配字母放入到不同的桶中。如果已经匹配到单词的最后一个字母,那么子序列单词数加 1。

算法实现

struct Node{
    
     //一个结构体就是一个word,这个word最多有5000个单词,每个最长50位
    char word[5000][51];
    int wordNum;
};

int numMatchingSubseq(char * S, char ** words, int wordsSize){
    
    
    struct Node barrels[26]; //设置26个桶,极端情况是所有的单词开头字母
    						 //都是一样的,这个桶依旧能装下
    int resultNum = 0;
    int strLen = strlen(S);
    int index = 0;
    char removeChar = 0;
    char secondChar = 0;
    for (int i = 0; i < 26; i++) {
    
         //初始化26个桶
        for (int j = 0; j < 5000; ++j) {
    
    
            memset(barrels[i].word[j], 0, 51);
        }
        barrels[i].wordNum = 0;    //桶内单词数用来判断该单词放在桶内位置,
            						   //也就是word中的第几行
    }
    if (wordsSize <= 5000){
    
    
        for (int i = 0; i < wordsSize; i++) {
    
      //将words存入到26个桶中
            int barrelIdx = words[i][0] - 'a';//判断属于哪个桶
            strcpy(barrels[barrelIdx].word[barrels[barrelIdx].wordNum], words[i]);//将该单词放到第几个桶(barrels[barrelIdx),该桶内第几个位置(word[barrels[barrelIdx].wordNum)
            barrels[barrelIdx].wordNum ++;     
        }
    }
    for (int j = 0; j < strLen; j++) {
    
      //遍历S,消首字母,做桶操作
        int doubleChar[26] = {
    
    0};
        char temp[51] = {
    
    0};
        removeChar = S[j];  
        index = (int)(removeChar - 'a');
        if (index < 26){
    
    
            for (int i = 0; i < barrels[index].wordNum; i++) {
    
    
                if (strlen(barrels[index].word[i]) > 1){
    
    
                    if (barrels[index].word[i][1] == barrels[index].word[i][0]){
    
      //首字母与第二字母相同
                        memset(temp, 0, 51);
                        strcpy(temp, barrels[index].word[i] + 1);//将首字母与第二字母相同的单词复制到temp
                        memset(barrels[index].word[doubleChar[index]], 0, 51);
                        strcpy(barrels[index].word[doubleChar[index]], temp);  //消首字母后复制到原始桶
                        doubleChar[index] ++;  //原始桶操作后单词个数记录
                    } else {
    
      
                        secondChar = barrels[index].word[i][1] - 'a'; //决定放到哪个新桶
                        if (secondChar < 26){
    
    
                            strcpy(barrels[secondChar].word[barrels[secondChar].wordNum], barrels[index].word[i] + 1);  //消首字母后复制到新桶
                            barrels[secondChar].wordNum ++;  //单词数加1
                            memset(barrels[index].word[i], 0, 51); //复制前word[i]所在位置清空
                        }
                    }
                } else if (strlen(barrels[index].word[i]) == 1){
    
      //最后一个字符
                    resultNum++;    //全部字符消除,子序列个数加1
                    memset(barrels[index].word[i], 0, 51);
                }
            }
            barrels[index].wordNum = doubleChar[index];  //遍历一个字符,操作一个桶,更新该桶wordNum
        }
    }
    return resultNum;
}

猜你喜欢

转载自blog.csdn.net/weixin_44910502/article/details/115125106
今日推荐