题目
给定一个由n个不重复非空字符串组成的数组,你需要按照以下规则为每个单词生成最小的缩写。
- 初始缩写由起始字母+省略字母的数量+结尾字幕组成。
- 若存在冲突,亦即多于一个单词有同样的缩写,则使用更长的前缀代替首字母,直到从单词到缩写的映射唯一。换而言之,最终的缩写必须只能映射到一个单词。
- 若缩写并不比原单词更短,则保留原样。
示例
输入:[“like”, “god”, “internal”, “me”, “internet”, “interval”, “intension”, “face”, “intrusion”]
输出: [“l2e”,“god”,“internal”,“me”,“i6t”,“interval”,“inte4n”,“f2e”,“intr4n”]
注意:
- n和每个单词的长度均不超过 400。
- 每个单词的长度大于 1。
- 单词只由英文小写字母组成。
- 返回的答案需要和原数组保持同一顺序。
贪心算法
首先给每个单词选择最短的缩写。然后我们对于所有重复的单词,我们增加这些重复项的长度。
比方说,我们有 “aabaaa”, “aacaaa”, “aacdaa”,我们首先得到 “a4a”, “a4a”, “a4a”。由于有重复项,我们将它们延长为 “aa3a”, “aa3a”, “aa3a”。此时仍然有重复项,所以我们继续延伸长度为 “aab2a”, “aac2a”, “aac2a”。最后两个字符串仍然是重复项,我们将它们继续延长得到 “aacaaa”, “aacdaa”。在此过程中,注意记录每个字符串解决冲突时前缀的长度,每次加加。
class Solution {
public:
string abbreviate(string word, int lenOfPre){
string suoxie = word.substr(0, lenOfPre); // 前缀
int len = word.length() - lenOfPre;
if( len <= 3 ){
suoxie += word.substr(lenOfPre);
}else{
suoxie += word[lenOfPre] + to_string(len-2) + word[word.length()-1]; // 注意数字与字符串的转换
}
// cout << suoxie << endl;
return suoxie;
}
vector<string> wordsAbbreviation(vector<string>& dict) {
int size = dict.size();
//先遍历一次,得初始缩写
vector<string> result;
for(int i=0; i<size; i++){
result.push_back(abbreviate(dict[i],0));
}
for(int i=0; i<size; i++){
//贪心:对于每个单词,查找并记录后面跟自己有冲突的,然后全部用更长的前缀代替。一直循环,直到没有冲突。
int lenOfPre = 1; // 使用更长的前缀代替
while(true){
vector<int> recordIndex; // 记录有冲突的单词的下标
for(int j=i+1; j<size; j++){
if( result[i] == result[j] ){
recordIndex.push_back(j); // 记录后面跟自己有冲突的
}
}
if(recordIndex.size()==0){
break;
}else{
recordIndex.push_back(i); // 别漏掉当前单词自己
for( int k=0; k<recordIndex.size(); k++ ){
int index = recordIndex[k];
result[index] = abbreviate(dict[index], lenOfPre);
}
lenOfPre++;
}
}
}
return result;
}
};
复杂度分析
- 时间复杂度:O( C^2 )。其中 C 是给定数组中所有字符串的字符总数目。
- 空间复杂度:O( C )。