题目:
如果交换字符串 X 中的两个不同位置的字母,使得它和字符串 Y 相等,那么称 X 和 Y 两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。
例如,"tars" 和 "rats" 是相似的 (交换 0 与 2 的位置); "rats" 和 "arts" 也是相似的,但是 "star" 不与 "tars","rats",或 "arts" 相似。
总之,它们通过相似性形成了两个关联组:{"tars", "rats", "arts"} 和 {"star"}。注意,"tars" 和 "arts" 是在同一组中,即使它们并不相似。形式上,对每个组而言,要确定一个单词在组中,只需要这个词和该组中至少一个单词相似。
给你一个字符串列表 strs。列表中的每个字符串都是 strs 中其它所有字符串的一个字母异位词。请问 strs 中有多少个相似字符串组?
示例 1:
输入:strs = ["tars","rats","arts","star"]
输出:2
示例 2:
输入:strs = ["omv","ovm"]
输出:1
提示:
1 <= strs.length <= 300
1 <= strs[i].length <= 300
strs[i] 只包含小写字母。
strs 中的所有单词都具有相同的长度,且是彼此的字母异位词。
备注:
字母异位词(anagram),一种把某个字符串的字母的位置(顺序)加以改换所形成的新词。
代码:
方法一——宽度优先遍历(bfs):
class Solution {
public:
bool judge(string a,string b){
int count=0;
int len=a.length();
for(int i=0;i<len;i++){
if(a[i]!=b[i]){
count+=1;
}
}
return count==0||count==2;
}
int numSimilarGroups(vector<string>& strs) {
int slen=strs.size();
vector<bool> visited(slen,false);
int count=0;
for(int k=0;k<slen;k++){
if(!visited[k]){
count+=1;
queue<int> q;
q.push(k);
visited[k]=true;
while(!q.empty()){
int qsize=q.size();
for(int i=0;i<qsize;i++){
int temp=q.front();q.pop();
for(int j=0;j<strs.size();j++){
if(visited[j])continue;
if(judge(strs[temp],strs[j])){
q.push(j);
visited[j]=true;
}
}
}
}
}
}
return count;
}
};
思路:
遍历所有没有被访问的字符串,每一个字符串都通过bfs,queue的方式,找它所有的相邻字符串,并标记为visited。通过技术,从循环进入访问了多少个字符串,这个次数,就是连通图的个数。
方法二——并查集,连通图
class Solution {
public:
int findroot(vector<int>& indexs,int i){
if(indexs[i]==i)return i;
int r= findroot(indexs,indexs[i]);
indexs[i]=r;
return r;
}
void insert(vector<int>& indexs,int i,int j){
int ri= findroot(indexs,i);
int rj= findroot(indexs,j);
if(ri==rj)return;
if(ri>rj){
indexs[ri]=rj;
}else{
indexs[rj]=ri;
}
}
bool judge(string a,string b){
int count=0;
int len=a.length();
for(int i=0;i<len;i++){
if(a[i]!=b[i]){
count+=1;
}
}
return count==0||count==2;
}
int numSimilarGroups(vector<string>& strs) {
int slen=strs.size();
vector<int> indexs(slen,0);
for(int i=0;i<slen;i++){
indexs[i]=i;
}
for(int i=0;i<slen;i++){
for(int j=i+1;j<slen;j++){
if(findroot(indexs,i)== findroot(indexs,j))continue;
if(judge(strs[i],strs[j])){
insert(indexs,i,j);
}
}
}
set<int> s;
for(int i=0;i<slen;i++){
int r= findroot(indexs,i);
if(!s.count(r)){
s.insert(r);
}
}
return s.size();
}
};
思路:两两遍历所有可能的字符串对,如果两个字符串只有两个字母不同或者全部相同,即相似,那么把他们插到并查集中。最后使用一个set统计,并查集中,有多少个不同的根的数目,这个就是题目中的不同相似字符串组的个数。