给定一个单词列表,我们将这个列表编码成一个索引字符串 S 与一个索引列表 A。
例如,如果这个列表是 [“time”, “me”, “bell”],我们就可以将其表示为 S = “time#bell#” 和 indexes = [0, 2, 5]。
对于每一个索引,我们可以通过从字符串 S 中索引的位置开始读取字符串,直到 “#” 结束,来恢复我们之前的单词列表。
那么成功对给定单词列表进行编码的最小字符串长度是多少呢?
示例 1:
输入: words = [“time”, “me”, “bell”]
输出: 10
说明: S = “time#bell#” , indexes = [0, 2, 5] 。
个人思路:首先要理解题意,假设只有两个词A,B 那么就要检查A是否是B的后缀,或者B是A的后缀,这样显然麻烦,我们每次都要互相判断A或B是否是其的后缀,可以想到排序,这样就能保证判断一次, 以单词的末尾进行排序显然是不方便的,所以可以考虑先将单词反转,然后进行排序。
比如先有单词组
string[] words = { "outint", "like", "int", "dislike", "nt" };
首先单词倒序
然后单词组排序-正序
单词组排序-倒序
经过排序后,找相同值(前缀)就方便多了,只要比价相邻值就行了,这里要注意的是如果你是采用正序排序遍历比较时需要从后向前([4]=>[3]=>...=>[0])
,倒序就从前向后([0]=>[1]=>...=>[4])
,至于原因看上面的图很好明白。
C#
class Program
{
static void Main(string[] args)
{
string[] words = { "outint", "like", "int", "dislike", "nt" };
Console.WriteLine(MinimumLengthEncoding(words));
Console.Read();
}
public static int MinimumLengthEncoding(string[] words)
{
/*首先要理解题意,假设只有两个词A,B
* 那么就要检查A是否是B的后缀,或者B是A的后缀,
* 这样显然麻烦,我们每次都要互相判断A或B是否是其的后缀,
* 可以想到排序,这样就能保证判断一次,
* 以单词的末尾进行排序显然是不方便的,所以可以考虑先将单词反转,然后进行排序
*/
//1.使用HashSet<>和SortedSet<>容器时间会明显变长,因为每次加入新元素都要排序去重,
//所以这里使用List<>
List<string> wordList = new List<string>();
//2-1.遍历所有单词,将其反转
foreach (string word in words)
{
//添加到List中
wordList.Add(new string(word.Reverse().ToArray()));
}
//2-2去重,倒序
//倒序,保证相同前缀的单词长的在前面
wordList = wordList.Distinct().OrderByDescending(reWord => reWord).ToList();
//3.首位作为初始值
string curStr = wordList.First(); //初始索引字符串
int count = curStr.Length; //初始索引字符串长度
//4.遍历单词集
for (int i = 0; i < wordList.Count - 1; i++)
{
//加上每次比较后的结果,ref保证当前索引字符串更新
count += CompareWords(ref curStr, wordList.ElementAt(i + 1));
}
//5.加上最后一个#
return ++count;
}
/// <summary>
/// 比较是否是前缀
/// </summary>
/// <param name="curStr">当前索引字符串</param>
/// <param name="nextWord">下一个准备比较单词</param>
/// <returns></returns>
private static int CompareWords(ref string curStr, string nextWord)
{
int cnt = 0;
//4-1.找出两个词中较短的数值
int min = Math.Min(curStr.Length, nextWord.Length);
//4-2.从后向前检查,
//因为判断是否前缀,所以最后不同就不是前缀了,前面的就不用判断了
for (int i = min - 1; i >= 0; i--)
{
//i==0即直到最后一个也符合,即next是cur的前缀
if (curStr[i] == nextWord[i] && i == 0)
{
cnt = 0;
}
//不同,即不是前缀,直接跳出
else if (curStr[i] != nextWord[i])
{
curStr = nextWord + curStr;
cnt = nextWord.Length + 1;//有一个#
break;
}
}
//4-3.最后返回本次计算后需要的长度
return cnt;
}
}
不同集合需要的时间