题目描述:
如果我们交换字符串 X 中的两个不同位置的字母,使得它和字符串 Y 相等,那么称 X 和 Y 两个字符串相似。
例如,"tars" 和 "rats" 是相似的 (交换 0 与 2 的位置); "rats" 和 "arts" 也是相似的,但是 "star" 不与 "tars","rats",或 "arts" 相似。
总之,它们通过相似性形成了两个关联组:{"tars", "rats", "arts"} 和 {"star"}。注意,"tars" 和 "arts" 是在同一组中,即使它们并不相似。形式上,对每个组而言,要确定一个单词在组中,只需要这个词和该组中至少一个单词相似。
我们给出了一个不包含重复的字符串列表 A。列表中的每个字符串都是 A 中其它所有字符串的一个字母异位词。请问 A 中有多少个相似字符串组?
示例:
输入:["tars","rats","arts","star"]
输出:2
提示:
A.length <= 2000
A[i].length <= 1000
A.length * A[i].length <= 20000
A 中的所有单词都只包含小写字母。
A 中的所有单词都具有相同的长度,且是彼此的字母异位词。
此问题的判断限制时间已经延长。
备注:
字母异位词[anagram],一种把某个字符串的字母的位置(顺序)加以改换所形成的新词。
思路:
这是一个元素分类问题,可以拿并查集解决;但若把元素看作图上一个个结点,满足相似条件的就连通,那么这就是一个求最多连通域个数的问题,可以拿bfs解决。
首先是并查集的思路:先将所有元素去重,将所有元素的根设置为本身,即本身认为是一个集合,然后遍历所有元素两两比较,若满足相似条件,合并这两集合,最终输出集合数。时间复杂度为O(N²)。
代码如下:
class Solution
{
public:
vector<int> parent;
int numSimilarGroups(vector<string> &A)
{
unordered_set<string> s(A.begin(),A.end());
vector<string> v;
for(auto it=s.begin();it!=s.end();++it)
v.push_back(*it);
int Vsize=v.size(),ans;
ans=v.size();
parent=vector<int>(Vsize);
for(int i=0;i<Vsize;++i)
parent[i]=i;
for(int i=0;i<Vsize;++i)
for(int j=i+1;j<Vsize;++j)
{
if(findParent(i)!=findParent(j)&&cmp(v[i],v[j]))
{
merge(i,j);
--ans;
}
}
return ans;
}
int findParent(int x)
{
while(x!=parent[x])
x=parent[x];
return x;
}
void merge(int x,int y)
{
x=findParent(x);
y=findParent(y);
parent[y]=x;
}
bool cmp(string &str1,string &str2)
{
int cnt=0;
for(int i=str1.size()-1;i>=0;--i)
{
if(str1[i]!=str2[i]) ++cnt;
if(cnt>2) return false;
}
return true;
}
};
然后是bfs的思路:
首先也是对所有元素进行去重,对所有元素进行遍历,若该元素没有被访问,则对该元素以及该元素连通区域进行bfs(其实dfs也是可以的,主要目的就是访问完整个连通域并标记),然后集合个数加一。访问一个连通域的一个元素的时候,遍历未被访问的元素,满足相似条件则添加到队列中)。时间复杂度也为O(N²)。
代码如下:
class Solution
{
public:
bool cmp(string A,string &B)
{
int count=0;
for(int i=B.size()-1;i>=0;--i)
{
if(A[i]!=B[i]) ++count;
if(count>2)return false;
}
return true;
}
int numSimilarGroups(vector<string>& A)
{
int ans=0;
unordered_set<string> s(A.begin(),A.end());
for(string &str:A)
{
if(s.find(str)==s.end())
continue;
queue<string> q;
++ans;
s.erase(str);
q.push(str);
while(!q.empty())
{
string front=q.front();
for(auto it=s.begin();it!=s.end();)
{
if(cmp(*it,front))
{
q.push(*it);
s.erase(it++);
}
else
++it;
}
q.pop();
}
}
return ans;
}
};