LeetCode: Group Shifted Strings | CrazyEgg
Given a string, we can "shift" each of its letter to its successive letter, for example: "abc" -> "bcd". We can keep "shifting" which forms the sequence:
Given a list of strings which contains only lowercase alphabets, group all strings that belong to the same shifting sequence.
[ ["abc","bcd","xyz"], ["az","ba"], ["acef"], ["a","z"] ]
Note: For the return value, each inner list’s elements must follow the lexicographic order.
X.
https://discuss.leetcode.com/topic/20722/my-concise-java-solution/
https://discuss.leetcode.com/topic/37916/5ms-java-solution
Two suggestions: (1) Use a
http://www.chenguanghe.com/group-shifted-strings/
可以看出, 当一个string的字符之间的距离决定了shift后的string. 所以对字符之间的距离进行记录, 当做key, 就可以找到. 这里先取一下string首字母当做offset, 然后算距离. 最后记录在一个key(string)中.
https://leetcode.com/discuss/64979/simple-solution-in-java-with-detailed-explaination
public List<List<String>> groupStrings(String[] strings) { List<List<String>> res = new ArrayList<List<String>>(); HashMap<List<Integer>, List<String>> map = new HashMap<List<Integer>, List<String>>(); for(int i=0; i<strings.length; i++) { List<Integer> curKey = new ArrayList<Integer>(); String str = strings[i]; int length = str.length(); curKey.add(length); for(int j=1; j<length; j++) { int offset = str.charAt(j) - str.charAt(j-1); int val = offset > 0 ? offset : 26 + offset; curKey.add(val); } if (map.containsKey(curKey)) { List<String> tmp = map.get(curKey); tmp.add(str); } else { List<String> tmp = new ArrayList<String>(); tmp.add(str); res.add(tmp); map.put(curKey, tmp); } } for(int i=0; i<res.size(); i++) { List<String> tmp = res.get(i); Collections.sort(tmp); } return res; }
https://discuss.leetcode.com/topic/20755/1-4-lines-in-java
public List<List<String>> groupStrings(String[] strings) { return new ArrayList(Stream.of(strings).sorted().collect(Collectors.groupingBy( s -> s.chars().mapToObj(c -> (c - s.charAt(0) + 26) % 26) .collect(Collectors.toList()) )).values()); }
Read full article from LeetCode: Group Shifted Strings | CrazyEgg
Given a string, we can "shift" each of its letter to its successive letter, for example: "abc" -> "bcd". We can keep "shifting" which forms the sequence:
1
| "abc" -> "bcd" -> ... -> "xyz"
|
Given a list of strings which contains only lowercase alphabets, group all strings that belong to the same shifting sequence.
For example.
given: ["abc", "bcd", "acef", "xyz", "az", "ba", "a", "z"], Return:[ ["abc","bcd","xyz"], ["az","ba"], ["acef"], ["a","z"] ]
Note: For the return value, each inner list’s elements must follow the lexicographic order.
X.
https://discuss.leetcode.com/topic/20722/my-concise-java-solution/
https://discuss.leetcode.com/topic/37916/5ms-java-solution
Two suggestions: (1) Use a
StringBuilder
instead of key += c;
(2) Abstract key encoding logic into one function for better readability. public List<List<String>> groupStrings(String[] ss) {
Map<String, List<String>> map = new HashMap<>();
for(String s: ss){
String key = getTag(s);
List<String> value = map.get(key);
if(value == null){
value = new ArrayList<>();
map.put(key, value);
}
value.add(s);
}
List<List<String>> ret = new ArrayList<>();
for(List<String> lst: map.values()){
Collections.sort(lst); // dont forget to sort.
ret.add(lst);
}
return ret;
}
String getTag(String s){
int diff = (int)s.charAt(0) - (int)'a';
StringBuilder sb = new StringBuilder();
for(char c: s.toCharArray())
sb.append((c+26-diff)%26);
return sb.toString();
}
http://www.geeksforgeeks.org/group-shifted-string/string getDiffString(string str)
{
string shift =
""
;
for
(
int
i = 1; i < str.length(); i++)
{
int
dif = str[i] - str[i-1];
if
(dif < 0)
dif += ALPHA;
// Representing the difference as char
shift += (dif +
'a'
);
}
// This string will be 1 less length than str
return
shift;
}
// Method for grouping shifted string
void
groupShiftedString(string str[],
int
n)
{
// map for storing indices of string which are
// in same group
map< string, vector<
int
> > groupMap;
for
(
int
i = 0; i < n; i++)
{
string diffStr = getDiffString(str[i]);
groupMap[diffStr].push_back(i);
}
// iterating through map to print group
for
(
auto
it=groupMap.begin(); it!=groupMap.end();
it++)
{
vector<
int
> v = it->second;
for
(
int
i = 0; i < v.size(); i++)
cout << str[v[i]] <<
" "
;
cout << endl;
}
}
http://www.chenguanghe.com/group-shifted-strings/
可以看出, 当一个string的字符之间的距离决定了shift后的string. 所以对字符之间的距离进行记录, 当做key, 就可以找到. 这里先取一下string首字母当做offset, 然后算距离. 最后记录在一个key(string)中.
public List<List<String>> groupStrings(String[] strings) {
List<List<String>> res = new ArrayList<List<String>>();
HashMap<String, List<String>> maps = new HashMap<>();
for(int i = 0 ; i < strings.length; i++) {
String key = "";
int offset = strings[i].charAt(0) - 'a';
for(int j = 1; j < strings[i].length(); j++) {
key+= (strings[i].charAt(j) - offset + 26) % 26;
}
if(!maps.containsKey(key))
maps.put(key,new ArrayList<>());
maps.get(key).add(strings[i]);
}
for(List<String> list : maps.values()){
Collections.sort(list);
res.add(list);
}
return res;
}
http://buttercola.blogspot.com/2015/08/leetcode-group-shifted-strings.html
The problem looks quite like the grouping anagrams. So the idea is the same: for each string, find out its "original" format, and check if the hash map contains this original string. If yes, put into the map.
==> It's better to first put each string to its group, then sort each group.
https://leetcode.com/discuss/50557/4ms-easy-c-solution-with-explanations
The problem looks quite like the grouping anagrams. So the idea is the same: for each string, find out its "original" format, and check if the hash map contains this original string. If yes, put into the map.
==> It's better to first put each string to its group, then sort each group.
public
class
Solution {
public
List<List<String>> groupStrings(String[] strings) {
List<List<String>> result =
new
ArrayList<List<String>>();
if
(strings ==
null
|| strings.length ==
0
) {
return
result;
}
Arrays.sort(strings,
new
LexComparator());
Map<String, List<String>> map =
new
HashMap<String, List<String>>();
for
(String s : strings) {
StringBuffer sb =
new
StringBuffer();
int
distance = Character.getNumericValue(s.charAt(
0
)) -
'a'
;
for
(
int
i =
0
; i < s.length(); i++) {
int
val = Character.getNumericValue(s.charAt(i)) - distance;
val = val <
'a'
? val +
26
: val;
char
ori = (
char
) val;
sb.append(ori);
}
String original = sb.toString();
if
(map.containsKey(original)) {
List<String> list = map.get(original);
list.add(s);
map.put(original, list);
}
else
{
List<String> list =
new
ArrayList<String>();
list.add(s);
map.put(original, list);
}
}
// Iterate the map
Iterator it = map.entrySet().iterator();
while
(it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
result.add((List<String>) pair.getValue());
}
return
result;
}
private
class
LexComparator
implements
Comparator<String> {
@Override
public
int
compare(String a, String b) {
if
(a.length() != b.length()) {
return
a.length() - b.length();
}
for
(
int
i =
0
; i < a.length(); i++) {
if
(a.charAt(i) != b.charAt(i)) {
return
a.charAt(i) - b.charAt(i);
}
}
return
0
;
}
}
}
this manner is adopted: for a string
s
of length n
, we encode its shifting feature as "s[1] - s[0], s[2] - s[1], ..., s[n - 1] - s[n - 2],"
.
vector<vector<string>> groupStrings(vector<string>& strings) {
unordered_map<string, vector<string> > mp;
for (string s : strings)
mp[shift(s)].push_back(s);
vector<vector<string> > groups;
for (auto m : mp) {
vector<string> group = m.second;
sort(group.begin(), group.end());
groups.push_back(group);
}
return groups;
}
private:
string shift(string& s) {
string t;
int n = s.length();
for (int i = 1; i < n; i++) {
int diff = s[i] - s[i - 1];
if (diff < 0) diff += 26;
t += 'a' + diff + ',';
}
return t;
}
public List<List<String>> groupStrings(String[] strings) { assert strings != null : "null array"; List<List<String>> result = new ArrayList<>(); Map<String, List<String>> map = new HashMap<>(); for (String s : strings) { String code = getFeatureCode(s); List<String> val; if (!map.containsKey(code)) { val = new ArrayList<>(); } else { val = map.get(code); } val.add(s); map.put(code, val); } for (String key : map.keySet()) { List<String> val = map.get(key); Collections.sort(val); result.add(val); } return result; } private String getFeatureCode(String s) { StringBuilder sb = new StringBuilder(); sb.append("#"); for (int i = 1; i < s.length(); i++) { int tmp = ((s.charAt(i) - s.charAt(i - 1)) + 26) % 26; sb.append(tmp).append("#"); } return sb.toString(); }
https://leetcode.com/discuss/64979/simple-solution-in-java-with-detailed-explaination
public List<List<String>> groupStrings(String[] strings) { List<List<String>> res = new ArrayList<List<String>>(); HashMap<List<Integer>, List<String>> map = new HashMap<List<Integer>, List<String>>(); for(int i=0; i<strings.length; i++) { List<Integer> curKey = new ArrayList<Integer>(); String str = strings[i]; int length = str.length(); curKey.add(length); for(int j=1; j<length; j++) { int offset = str.charAt(j) - str.charAt(j-1); int val = offset > 0 ? offset : 26 + offset; curKey.add(val); } if (map.containsKey(curKey)) { List<String> tmp = map.get(curKey); tmp.add(str); } else { List<String> tmp = new ArrayList<String>(); tmp.add(str); res.add(tmp); map.put(curKey, tmp); } } for(int i=0; i<res.size(); i++) { List<String> tmp = res.get(i); Collections.sort(tmp); } return res; }
Nice! Two suggestions: (1) Use a
StringBuilder
instead of key += c;
(2) Abstract key encoding logic into one function for better readability.public List<List<String>> groupStrings(String[] strings) { return new ArrayList(Stream.of(strings).sorted().collect(Collectors.groupingBy( s -> s.chars().mapToObj(c -> (c - s.charAt(0) + 26) % 26) .collect(Collectors.toList()) )).values()); }
Read full article from LeetCode: Group Shifted Strings | CrazyEgg