单词的压缩编码-自定义排序(力扣第820T)

题目:
给定一个单词列表,我们将这个列表编码成一个索引字符串 S 与一个索引列表 A。
例如,如果这个列表是 [“time”, “me”, “bell”],我们就可以将其表示为 S = “time#bell#” 和 indexes = [0, 2, 5]。
对于每一个索引,我们可以通过从字符串 S 中索引的位置开始读取字符串,直到 “#” 结束,来恢复我们之前的单词列表。
那么成功对给定单词列表进行编码的最小字符串长度是多少呢?

示例:
输入: words = [“time”, “me”, “bell”]
输出: 10
说明: S = “time#bell#” , indexes = [0, 2, 5] 。

思路:
首先这个题理解起来有点难度,因为time后面有me,所以复用me就可以,me的部分就可以压缩掉。举个更好理解的例子:[“me”,“time”,“ime”];压缩完后,time#,索引为[2,0,1]。每次读的时候从当前位置读到#即可,所以从2位置到#,me;从0位置到#,time;从1位置到#,ime。
这么说是不是就可以解释了。其实也就是如果该字串是某个字串的后子串。什么是后子串,就是该子串从后面能和某个字符串后面的匹配。比如:time和me,me能从后面e匹配,m匹配。那么me就是time的后子串。
所以这其实是个后子串匹配题。首先我们用暴力解法,申请一个数组,存放结果字符串,如果现在的字串是结果字符串里某个的子串,跳过,对比下一个;如果现在的字串在结果字串里有后子串,那么将该字符串覆盖进去,将少的长度补上。如果没有子串关系,直接放入结果字串。
暴力解法代码如下:

class Solution {
public:
    bool matchStr(string& res1,string& res2)
    {
        int len1=res1.length();
        int len2=res2.length();
        while(len1>=1&&len2>=1&&res1[len1-1]==res2[len2-1])
        {
            len1--;
            len2--;
        }
        if(len2==0)
        {
            return true;
        }
        return false;
    }
    int minimumLengthEncoding(vector<string>& words) {
        int length=words.size();
        if(length==1)
            return words[0].length()+1;
        vector<string> ans(2000);
        int resultSize=0;
        int index=0;
        for(int i=0;i<length;i++)
        {
            bool flg=0;
            for(int j=0;j<index;j++)
            {
                if(matchStr(words[i],ans[j]))
                {
                    resultSize+=words[i].length()-ans[j].length();
                    ans[j]=words[i];
                    flg=1;
                    break;
                }
                else if(matchStr(ans[j],words[i]))
                {
                    flg=1;
                    break;
                }
            }
            if(!flg)
            {
                ans[index]=words[i];
                index++;
                resultSize+=words[i].length()+1;
            }
        }
        return resultSize;
    }
};

从暴力解法可以看出,我们要进行两次对比,因为我们不确定谁是谁的子串。但其实,短的可能是子串,长的一定不会是子串。而且我们对比时是从后往前对比,所以我们可以按后面的字母先排序,一样则前一位排序。比如:time,lime,me,mad排完序-me,lime,time,mad。这里面me,lime怎么排,我们说过,长的一定不会是子串,所以我们在排序后,长的放在后面,这样我们只需要把前一个和后一个比就行了。
这里面有个自定义排序,之前做过,后来有点忘却了,今天重新复习一遍。cmp函数,参数(a,b),当返回结果为true时,不交换,false时,交换。看下面代码,当c1!=c2时,如果c1<c2,说明就是前面比后面小,不需要交换。len1<len2,也不需要。
canmatch函数是用来判断是否是子串的。
最后代码献上。

class Solution {
public:
    static bool cmp(string& tmp1, string& tmp2)
    {
        int len1 = tmp1.length();
        int len2 = tmp2.length();
        for (int i = 0; i < min(len1, len2); i++) {
            char c1 = tmp1[len1-1-i];
            char c2 = tmp2[len2-1-i];
            if (c1 != c2) {
                return c1 < c2;
            }
        }
        return len1<len2;
    }
    bool canmatch(string& res1,string& res2)
    {
        int len1=res1.length();
        int len2=res2.length();
        while(len1>=1&&res1[len1-1]==res2[len2-1])
        {
            len1--;
            len2--;
        }
        if(len1==0)
        {
            return true;
        }
        return false;
    }
    int minimumLengthEncoding(vector<string>& words) {
        int length=words.size();
        if(length==1)
            return words[0].length()+1;
        sort(words.begin(),words.end(),cmp);
        int count=0;
        for(int i=0;i<length;i++)
        {
            if(i+1<length&&canmatch(words[i],words[i+1]))
            {
                continue;
            }
            else
            {
                count+=words[i].length()+1;
            }
        }
        return count;
    }
};

这个题耗了大概一个小时的时间才完善了第二个代码,总结起来都是一些小细节方面的错误,所以编程还是一个考虑细节的东西。
加油,冲冲冲。

发布了22 篇原创文章 · 获赞 0 · 访问量 348

猜你喜欢

转载自blog.csdn.net/yanchenzhi/article/details/105170137