问题描述
给出长度相同的两个字符串:A
和 B
,其中 A[i] 和 B[i] 是一组等价字符。举个例子,如果 A = "abc"
且 B = "cde"
,那么就有 'a' == 'c', 'b' == 'd', 'c' == 'e'
。
等价字符遵循任何等价关系的一般规则:
- 自反性:’a’ == ‘a’
- 对称性:’a’ == ‘b’ 则必定有 ‘b’ == ‘a’
- 传递性:’a’ == ‘b’ 且 ‘b’ == ‘c’ 就表明 ‘a’ == ‘c’
例如,A
和 B
的等价信息和之前的例子一样,那么 S = "eed"
, "acd"
或 "aab"
,这三个字符串都是等价的,而 "aab"
是 S
的按字典序最小的等价字符串
利用 A
和 B
的等价信息,找出并返回 S
的按字典序排列最小的等价字符串。
问题理解
此问题是先要将字母分组,再将分组的字母当成字典来转换新字符串。
问题分析
用并查集,中间一个数组来过渡,这个数组存放每个字母对应的最小等价字母。
方法实现,分两个步骤,查找和合并。查找是指在数组中查找当前字母对应的最小字母;合并是指修改两个字母的最小字母为同一个最小字母。
方法复杂度是log(nlogn)。
其他
此题如果用vector,map来做非常麻烦,转化成字母来考虑,非常简单清晰,并查集思想相当牛逼。
链接
class Solution {
public:
vector<int> p; //p存储字母所在集合的最小字母值(字母ascii码-'a')
int find(int x)
{
if (p[x] == -1) { //如果最小字母是默认值
return x; //自己本身是最小值
}
else //否则
{
return p[x] = find(p[x]); //递归找到最小值,然后更新当前字母的最小值为最小值
}
}
void merge(int x, int y)
{
int min_x = find(x); //找到x的最小值
int min_y = find(y); //找到y的最小值
if (min_x == min_y) //相等
return; //已经合并,不用考虑
else if (min_x < min_y) //x小
{
p[min_y] = min_x; // y降低到x的最小值
}
else
{
p[min_x] = min_y; // x降低到y的最小值
}
}
string smallestEquivalentString(string A, string B, string S) {
p = vector<int>(26, -1); //初始值都为-1
int len = A.size();
string ansStr = "";
for(int i = 0; i < len; i ++)
{
merge(A[i] - 'a', B[i] - 'a'); //合并每组等价字母
}
for(char c : S)
{
ansStr += find(c - 'a') + 'a'; //找到当前字母的最小值加上'a'转化成字母输出。
}
return ansStr;
}
};