给定一个string数组dic及数组大小n,同时给定字典中的两个字符串串s和串t,为将s变到t,每次可以改变s中的任意一个字符,请返回由s到t变换所需的最少步数。同时需要满足在变换过程中的每个串都是字典中的串。若无法变换到t则返回-1。保证字符串长度均小于等于10,字典中字符串数量小于等于500。
测试样例:
["abc","adc","bdc","aaa”],4,”abc","bdc"
返回:2
给定一个string数组dic及数组大小n,同时给定字典中的两个字符串串s和串t,为将s变到t,每次可以改变s中的任意一个字符,请返回由s到t变换所需的最少步数。同时需要满足在变换过程中的每个串都是字典中的串。若无法变换到t则返回-1。保证字符串长度均小于等于10,字典中字符串数量小于等于500。
["abc","adc","bdc","aaa”],4,”abc","bdc"
返回:2
//参考了最前面的代码,按照自己的思路整理了一下; public: int countChanges(vector<string> dic, int n, string s, string t) { // write code here if(s==t) return 0;//如果两个单词相同,变换0次即可; vector<string>::iterator ite = find(dic.begin(),dic.end(),s); int result = BFS(s,t,dic); return result; } private: int BFS(string start,string end,vector<string> &dict) { if(start == end) return 0; // 存放单词和单词所在层次 queue<pair<string,int> > q; q.push(make_pair(start,0)); // 判断是否访问过 vector<string> visited; visited.push_back(start); while(!q.empty()) { pair<string,int> cur = q.front(); q.pop(); string word = cur.first; int size = word.size(); // 穷尽所有可能的变换 for(int i=0;i<size;++i) { string newWord(word); // 每次只变换一个字符,有26种可能 for(int j = 0;j < 26;++j) { newWord[i] = 'a'+j; // 判断之前访问过或者是否在字典里 vector<string>::iterator ite = find(dict.begin(),dict.end(),newWord); vector<string>::iterator ite2 = find(visited.begin(),visited.end(),newWord); // 找到目标返回 if((ite!=dict.end()&&(newWord == end)))//判断存在于字典中,并且和目标字符串相同 { return cur.second+1; } //没找到目标,那么就将该变换的字符串压入到visited中 if(ite2 == visited.end() && ite != dict.end()) { visited.push_back(newWord); q.push(make_pair(newWord,cur.second+1)); } } } } return -1; } };
/*图的最短路径问题,首先构造距离矩阵,然后Floyd算法即可*/ class Change { public: bool path(string a, string b) { int lenA = a.length(), lenB = b.length(); if (lenA != lenB) { return false; } int flag = 0; for (int i = 0; i < lenA; i++) { if (a[i] != b[i]) { if (flag == 0) { flag++; } else { return false; } } } return true; } int countChanges(vector<string> dic, int n, string s, string t) { int map[500][500]; //题中明确说了不超过500个,so int index_s, index_t; for (int i = 0; i < n; i++) { if (dic[i] == s) { index_s = i; } if (dic[i] == t) { index_t = i; } for (int j = i; j < n; j++) { if (i == j) map[i][j] = 0; else if (path(dic[i], dic[j])) { map[i][j] = map[j][i] = 1; } else { map[i][j] = map[j][i] = 500; //只有500个元素,最大距离不过500 } } } for (int k = 0; k < n; k++) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (map[i][k] + map[k][j] < map[i][j]) { map[i][j] = map[i][k] + map[k][j]; } } } } return (map[index_s][index_t] == 500) ? -1 : map[index_s][index_t]; } };
从目标字符串反推源字符串 #include<unordered_set> class Change { private: int countDifference(const string& s1, const string& s2) { int res = 0; for (int i = 0; i < s1.size(); ++i) { if (s1[i] != s2[i]) ++res; } return res; } public: int countChanges(vector<string> dic, int n, string src, string target) { if (src == target) return 1; if (src.size() != target.size()) return -1; vector<int> dp(n, INT_MAX); unordered_set<string> hs; for (auto& s : dic) if (s.size() == src.size()) hs.emplace(s); if (hs.find(target) == hs.end()) return -1; hs.erase(target); vector<string> cur{ target }; int res = 0; while (true) { ++res; vector<string> next; for (auto& s1 : cur) { for (auto& s2 : hs) { if (countDifference(s1, s2) == 1) { if (s2 == src) return res; next.emplace_back(s2); } } } if (next.empty()) break; for (auto& s : next) hs.erase(s); cur = next; } return -1; } };
import java.util.*; public class Change { public int countChanges(String[] dic, int n, String s, String t) { Map<String ,List<String>> graph=new HashMap<>(); Set<String> dicSet=new HashSet<>(Arrays.asList(dic)); for (String s1 : dic) { List<String> adj=getAdj(s1,dicSet); graph.put(s1,adj); } Queue<String> queue=new ArrayDeque<>(); Map<String ,Integer> vistied=new HashMap<>(); vistied.put(s,0); queue.add(s); while(!queue.isEmpty()){ String te=queue.poll(); if (te.equals(t)) return vistied.get(t); for (String s1 : graph.get(te)) { if (null==vistied.get(s1)){ vistied.put(s1, vistied.get(te) + 1); queue.add(s1); } } } return -1; } private List<String> getAdj(String s1, Set<String> dicSet) { List<String> rst=new ArrayList<>(); for (int i = 0; i < 26; i++) { for (int j = 0; j < s1.length(); j++) { StringBuilder builder=new StringBuilder(s1); builder.setCharAt(j, (char) ('a'+i)); if (dicSet.contains(builder.toString())&&!builder.toString().equals(s1)) rst.add(builder.toString()); } } return rst; } }
BSF搜索最短路径: class Change { public: bool isChanged(string a, string b) { int na = a.length(), nb = b.length(), diff = 0; if(na != nb) return false; for(int i = 0; i < na; i++) { if(a[i] != b[i]) diff++; } return (diff == 1); } int countChanges(vector<string> dic, int n, string s, string t) { // write code here if(s == t) return 0; map<string, bool> visit; //int size = dic.size(); for(int i = 0; i < n; i++) visit[dic[i]] = false; queue<string> q; int res = 0, cur = 0, cur_num = 1, count = 0; q.push(s); while(!q.empty()) { string temp = q.front(); q.pop(); if(temp == t) return res; //int len = temp.length(); for(int i = 0; i < n; i++) { if(!visit[dic[i]] && isChanged(temp, dic[i])) {//cout << dic[i] << endl; visit[dic[i]] = true; q.push(dic[i]); dic.erase(dic.begin() + i); i--; n--; count++; } } cur++; if(cur == cur_num) { cur = 0; cur_num = count; count = 0; res++; } } return -1; } };
Solution: 使用Queue进行BFS,同时用一个HashSet来存储已经访问过的字符串。每访问完一个字符串(queue.poll())就对该字符串进行替换:是否存在只变换一个字符的String在字典dic中,注意dic中有重复的字符串,所以我们可以使用一个HashSet来保存把重复的去掉。
public int countChanges(String[] dic, int n, String s, String t) { // write code here if(dic==null || n==0){ return 0; } HashSet<String> hash=new HashSet(); //记录访问过的string,同时可以判断某个string是否访问过 Queue<String> queue=new LinkedList<String>(); queue.offer(s); hash.add(s); int length=0; // 从0开始 while(!queue.isEmpty()){ length++; // 每进入循环一次,即步长+1 int size=queue.size(); for(int i=0;i<size;i++){ // 同一个level String curr = queue.poll(); for(String next: getNext(curr,dic)){ // 满足条件的变换了的字符串 if(hash.contains(next)){ continue; } if(next.equals(t)){ //找到t字符串了就return return length; } queue.offer(next); // 继续下一步的查找 hash.add(next); } } } return 0; } private List<String> getNext(String word, String[] dic){ List<String> result=new ArrayList<String>(); if(dic==null || dic.length==0) return result; Set<String> set=new HashSet<String>(); //由于dic中有重复的字符串,使用HashSet来去掉重复的 for(String s:dic){ set.add(s); } for(char ch='a';ch<='z';ch++){ for(int i=0;i<word.length();i++){ if(word.charAt(i)==ch){ continue; } String newWord=replace(word,i,ch); if(set.contains(newWord)){ result.add(newWord); } } } return result; } private String replace(String s,int i, char ch){ char[] arr=s.toCharArray(); arr[i]=ch; return new String(arr); }
//类似于二叉树的层次遍历,每次选取能修改一个字符达到的节点
class Change { public: int countChanges(vector dic, int n, string s, string t) { // write code here queue q; int min_step = 0; int level_num = 1; q.push(s); vector flag(n,0); string node_str; while(!q.empty()) { node_str = q.front(); q.pop(); level_num--; if(node_str == t) return min_step; for(int i=0;i<n;++i) { if(flag[i]==0 && checkOneChage(dic[i], node_str)) { flag[i] = 1; q.push(dic[i]); } } if(level_num==0) { min_step++; level_num = q.size(); } } return -1; } bool checkOneChage(string& s,string& t) { if(s.size() != t.size()) { return false; } int num = 0; for(int i=0;i<s.size();++i) { if(s[i] != t[i]) num++; } return num == 1; } };
class Change { public: int BFS(vector<string> dic, string s, string e){ if(s==e) return 0; queue<pair<string,int>> q; q.push(make_pair(s,0)); vector<string> v; v.push_back(s); while(!q.empty()){ pair<string,int> p = q.front(); q.pop(); string w = p.first; int n = w.length(); for(int i=0;i<n;i++){ string t(w); for(int j=0;j<26;j++){ t[i] = 'a'+j; if(t==e) return p.second+1; if(find(dic.begin(),dic.end(),t)!=dic.end() && find(v.begin(),v.end(),t)==v.end()){ v.push_back(t); q.push(make_pair(t, p.second+1)); } } } } return -1; } int countChanges(vector<string> dic, int n, string s, string t) { if(s==t) return 0; return BFS(dic,s,t); } };
class Change: def countChanges(self, dic, n, s, t): v, q, trace = set(), [s], {} v.add(s) while len(q) > 0: # BFS h = q.pop(0) for x in [x for x in dic if x not in v and self.diff(h, x)]: if x == t: k = 1 while h != s: k += 1 h = trace[h] return k else: q.append(x) v.add(x) trace[x] = h return -1 @staticmethod def diff(a, b): # 找出只有一个字母不同的单词 k = 0 if len(a) == len(b): for i in xrange(len(a)): if a[i] != b[i]: k += 1 elif k >= 2: return False return k == 1
用dfs做的,在其中有三点很重要:第一,减枝,如果发现长度大于之前的,则直接return; 第二,首先在原始数据中删除重复数据;第三;再找到目标字符串后直接返回当前函数,不在此 基础上继续dfs。 class Change { vector<string> vec; int num; int flag[505]; string aim; int res=-1; public: int countChanges(vector<string> dic, int n, string s, string t) { // write code here sort(dic.begin(),dic.end()); dic.erase(unique(dic.begin(),dic.end()),dic.end()); for(int i=0;i<dic.size();i++){ vec.push_back(dic[i]); } num=dic.size(); aim=t; res=0x3f3f3f3f; memset(flag,0,sizeof(flag)); for(int i=0;i<n;i++){ if(dic[i]==s) flag[i]=1; } dfs(s,0); if(res==0x3f3f3f3f) return -1; return res; } void dfs(string s,int route){ if(route>res) return; //减枝,这个很重要 if(s==aim){ if(route<res) res=route; return; //这里找到之后return } vector<int> vint; for(int i=0;i<num;i++){ if(isymble(s,vec[i])){ vint.push_back(i); } } for(int i=0;i<vint.size();i++){ if(flag[vint[i]]==1) continue; else{ flag[vint[i]]=1; dfs(vec[vint[i]],route+1); flag[vint[i]]=0; } } } bool isymble(string str1,string str2){ int len1=str1.length(); int len2=str2.length(); if(len1!=len2) return false; int diseq=0; for(int i=0;i<len1;i++){ if(str1[i]!=str2[i]) diseq++; } if(diseq==0) return false; if(diseq==1) return true; return false; } };
Python解法:说起来怎么现在只剩python2的编译器了,MD中文注释一直报错...
# -*- coding:utf-8 -*- #可以看成从s到t是否可达,故用BFS class Change: def countChanges(self, dic, n, s, t): path = dict()#用dict来存储target->start的路径 dic = set(dic)#去除dic中重复的节点 #用BFS queue = [s] visited = set() all_count = []#所有路径的长度 while queue: current_node = queue.pop(0) # 对于current_node的所有邻居 for neibor in self.getNeibors(current_node, dic, visited): if neibor == t: path[neibor] = current_node all_count.append( self.get_start_to_target_count(path, s, t)) else: queue.append(neibor) visited.add(neibor)#这里是neibor而不是current_node path[neibor] = current_node #返回最小的长度 if not all_count: return -1 return min(all_count) #根据path计算count def get_start_to_target_count(self, path, start, target): count = 0 while not target == start: count += 1 target = path[target] return count #获取所有邻居 def getNeibors(self, current_node, dic, visited): neibors = [] for node in dic: #没有访问过的且差值相差1的 if not node in visited and self.diffCount(current_node, node): neibors.append(node) return neibors #计算差值 def diffCount(self, current_node, node): if not len(current_node) == len(node): return False count = 0 #每一位都比较 for i in range(len(current_node)): if not current_node[i] == node[i]: count += 1 if count > 1: return False if count == 1: return True
//BFS int countChanges(vector<string> dic, int n, string s, string t) { set<string>res(dic.begin(),dic.end()); res.erase(s); queue<string>q; q.push(s); int f = 0,ans = 0; while(res.size()){ int len = q.size(); while(len--){ string start = q.front(); if(start == t)return ans; q.pop(); vector<string>tmp; for(auto x:res){ if(valid(x,start)){ f = 1; q.push(x); tmp.push_back(x); } } for(auto x:tmp)res.erase(x); } if(f)ans++; f = 0; } return -1; } bool valid(string s1,string s2){ int len = s1.size(); if(len!=s2.size())return false; int cnt = 0; for(int i = 0;i<len;++i){ if(s1[i]!=s2[i])++cnt; } return cnt == 1; }
1.由于变换法则是改变任意一个字符 故不存在添加 删除操作 所以长度不相同的词必定不能 2.两个词相同 直接返回0 3.若两个词长度为n 则需要先把dict中长度不是n的单词过滤掉 4.以s为起点,遍历由变换关系组建的图,因为是最少次数 故是宽度优先 采用队列 5.后面两个辅助函数负责找到字典中与词邻接的词 这里的邻接关系为 若s到t能通过 变化一个字符获得 则邻接 class Change: def countChanges(self, dic, n, s, t): """ 最短路径 宽度优先 数据结构为队列 """ if s == t: return 0 elif len(s) != len(t): return -1 else: length = len(s) dic_ = [] for word in dic: if len(word) == length: dic_.append(word) visited = [] queue = self.getlist(s,dic_) dis = 1 while queue: queue_ = [] while queue: cur = queue[0] queue.remove(queue[0]) if cur in visited: continue else: if cur == t: return dis else: visited.append(cur) queue_ += self.getlist(cur,dic_) dis += 1 queue = queue_ return -1 def is_common_1(self,s1,s2): ## 判断两个词之间是否可以变1来转化 length = len(s1) count = 0 for i in range(length): if s1[i] != s2[i]: count += 1 return (count == 1) def getlist(self,s,dic): list1 = [] for s_ in dic: if self.is_common_1(s,s_): list1.append(s_) else: continue return list1
1. 利用iterator中的find函数,判断当前元素是否在容器中;
2. 利用BFS,广度优先遍历,遍历所有可能情况;
3. 利用BFS,需要记录已经访问过的元素,防止二次访问;
import java.util.*;
public class Change {
public int countChanges(String[] dic, int n, String s, String t) {
if (dic == null || n <= 0 || s.length() != t.length())
return -1;
if (s.equals(t))
return 0;
Map<String, Integer> map = new HashMap<>();//记录当前字符串被改变的次数
LinkedList<String> queue = new LinkedList<>();//BFS的队列
map.put(s, 0);
queue.add(s);
while (!queue.isEmpty()) {
String top = queue.peek();
List<String> adjs = getAdjStr(dic, n, top);//获取当前字符串的邻接字符串(只有一位不相同的字符串)
for (String adj : adjs) {
if (adj.equals(t)) {//当前变换已经与目的字符串一致
return map.get(top) + 1;//因为是邻接的关系,所以需要+1
} else {
if (map.get(adj) == null) {
map.put(adj, map.get(top) + 1);//因为是邻接的关系,所以需要+1
queue.add(adj);
}
}
}
}
return -1;
}
public List<String> getAdjStr(String[] dic, int n, String s) {
List<String> adjs = new ArrayList<>();//存放邻接字符串
String t;
int count;//记录不同字符的个数
for (int i = 0; i < n; i++) {
t = dic[i];
count = 0;
if (s.length() != t.length())//长度不一致的不做判断
continue;
int j = 0;
for (; j < s.length(); j++) {//逐位比较,如果不同位数超过1,直接结束
if (s.charAt(j) != t.charAt(j)) {
if (count != 0)
break;
count++;
}
}
if (j != s.length() || count != 1)//判断是否遍历到字符串s的最后一位,或者不是恰好改变了一个字符
continue;
adjs.add(t);
}
return adjs;
}
}
// 相对于其他答案,这个答案应该是稍微优化一点的版本。 // 理由是 他们在找newword 是否存在于已给字符数组中,用的是遍历, // 而这个是用的map。。。。 // 还是十分佩服能总结出方法的人。 class Change { public: int countChanges(vector<string> dic, int n, string s, string t) { // write code here // get a string map first map<string,int> stringmap; for (int i = 0 ; i != n ; i ++) stringmap[dic[i]] = 1; return BFS(s,t,stringmap); } int BFS(string s, string t, map<string,int> &stringmap) { if (s == t) return 0; queue<pair<string,int>> q; q.push(make_pair(s,0)); map<string,int> visited; visited[s] = 1; while(!q.empty()) { pair<string,int> cur = q.front(); q.pop(); for (int i = 0 ; i != cur.first.length(); i++) { string newword = cur.first; for (int j = 0 ; j != 26 ; j++) { newword[i] = 'a' + j; if (newword == t && stringmap.find(newword) != stringmap.end()) { return cur.second + 1; } if (visited.find(newword) == visited.end() && stringmap.find(newword) != stringmap.end()) { visited[newword] = 1; q.push(make_pair(newword,cur.second + 1)); } } } } return -1; } };
import java.util.*; /*思路:参考BFS算法,遍历到一个字符串,将该字符串转换一次并且数组中存在的与该字符串不等的字符串入队列 当队列遍历到一层中存在可以转换成目标字符串时跳出循环并将count+1即为结果 */ public class Change { public int countChanges(String[] dic, int n, String s, String t) { Queue<String> queue=new ArrayDeque<String>(); ArrayList<String> list=new ArrayList<String>();//用于标记已访问过的字符,避免重复访问 queue.offer(s); queue.offer("#");//用于标记层数 int count=0; boolean res=false;//用于记录是否存在转换 while (!queue.isEmpty()) { String temp=queue.poll(); if (temp.equals("#")) {//当遍历到“#”时,层数+1即转换操作+1 count++; temp=queue.poll(); queue.offer("#"); } list.add(temp); for (int i = 0; i < dic.length; i++) { if (temp!=dic[i]&&!list.contains(dic[i])) { if (isContain(temp, dic[i])) { if (dic[i].equals(t)) {//判断是否等于目标字符串 res=true; break; } if (!queue.contains(dic[i])) {//满足转换条件的结果可能重复,则去重 queue.offer(dic[i]); } } } } if (res) { break; } } if (res) { return count+1; }else { return -1; } } /*判断一个字符串是否存在一次转换就形成另一个字符串*/ public boolean isContain(String s1,String s2){ boolean res=true; char[] c1=s1.toCharArray(); char[] c2=s2.toCharArray(); if (s1.length()!=s2.length()) { return false; }else { for (int i = 0; i < c2.length; i++) { if (c1[i]!=c2[i]) { c1[i]=c2[i]; if (s2.equals(String.valueOf(c1))) { break; }else { res=false; break; } } } return res; } } }
//word ladder,bfs
class Change {
public:
int countChanges(vector<string> dic, int n, string s, string t) {
// write code here
map<string,int> co;
for(int i=0;i<n;i++)
{
if(co.count(dic[i]))
co[dic[i]]++;
else
co[dic[i]]=1;
}
queue<string> tp;
tp.push(s);
int num=1;
int cnt;
int count=0;
bool find=false;
while(!find&&num)
{
cnt=num;
num=0;
while(!find&&cnt--)
{
string cur=tp.front();
tp.pop();
for(int i=0;i<int(cur.length())&&!find;i++)
{
char w=cur[i];
for(char c='a';c<='z';c++)
{
cur[i]=c;
if(cur==t)
{
find=true;
break;
}
else if(co.count(cur))
{
num++;
tp.push(cur);
co[cur]--;
if(co[cur]==0)
co.erase(cur);
}
}
cur[i]=w;
}
}
count++;
}
if(!find)
return -1;
return count;
}
};