给你一个下标从 0 开始的字符串数组 words 。每个字符串都只包含 小写英文字母 。words 中任意一个子串中,每个字母都至多只出现一次。
如果通过以下操作之一,我们可以从 s1 的字母集合得到 s2 的字母集合,那么我们称这两个字符串为 关联的 :
往 s1 的字母集合中添加一个字母。
从 s1 的字母集合中删去一个字母。
将 s1 中的一个字母替换成另外任意一个字母(也可以替换为这个字母本身)。
数组 words 可以分为一个或者多个无交集的 组 。如果一个字符串与另一个字符串关联,那么它们应当属于同一个组。
注意,你需要确保分好组后,一个组内的任一字符串与其他组的字符串都不关联。可以证明在这个条件下,分组方案是唯一的。
请你返回一个长度为 2 的数组 ans :
ans[0] 是 words 分组后的 总组数 。
ans[1] 是字符串数目最多的组所包含的字符串数目。
示例 1:
输入:words = ["a","b","ab","cde"]
输出:[2,3]
解释:
- words[0] 可以得到 words[1] (将 'a' 替换为 'b')和 words[2] (添加 'b')。所以 words[0] 与 words[1] 和 words[2] 关联。
- words[1] 可以得到 words[0] (将 'b' 替换为 'a')和 words[2] (添加 'a')。所以 words[1] 与 words[0] 和 words[2] 关联。
- words[2] 可以得到 words[0] (删去 'b')和 words[1] (删去 'a')。所以 words[2] 与 words[0] 和 words[1] 关联。
- words[3] 与 words 中其他字符串都不关联。
所以,words 可以分成 2 个组 ["a","b","ab"] 和 ["cde"] 。最大的组大小为 3 。
示例 2:
输入:words = ["a","ab","abc"]
输出:[1,3]
解释:
- words[0] 与 words[1] 关联。
- words[1] 与 words[0] 和 words[2] 关联。
- words[2] 与 words[1] 关联。
由于所有字符串与其他字符串都关联,所以它们全部在同一个组内。
所以最大的组大小为 3 。
提示:
1 <= words.length <= 2 * 10^4
1 <= words[i].length <= 26
words[i] 只包含小写英文字母。
words[i] 中每个字母最多只出现一次。
解题思路
状态压缩+并查集+map
1、状态压缩:由于每一字符串都是有26个不重复的小写字母组成,可以把每个字母当作一位2进制,出现则该位为1,不出现则该位为0,进行字符串状态压缩word ->key。
2、并查集:利用map进行存储,MapKey存储每个字符串key的并查集上一层的父节点,MapNum存储每个字符串key的并查集的数量。
通过find_key(Key, NextKey, MapKey) 查找Key的最终父节点,注意优化点:重置链表上Key最终父节点为合并并查集后的父节点NextKey,要不然可能会TLE。
3、在处理的过程中预处理最终的并查集数Size(初始为字符串数组的数量,合并一次减一),最大并查集数Len(初始为1,每次合并后取最大值)。
-spec group_strings(Words :: [unicode:unicode_binary()]) -> [integer()].
group_strings(Words) ->
Map = maps:new(),
MapNum = maps:new(),
Seq = lists:seq(0, 25),
Size = length(Words),
do_init(Words, Seq, Map, MapNum, Size, 1)
.
%map1 = {Key = Word_num, Value = father},
%map2 = {key = Word_num, value = size}
do_init([], Seq, MapKey, MapNum, Size, Len) ->
[Size,Len];
do_init([W | Words], Seq, MapKey, MapNum, Size, Len) ->
List = [Ch - 97 || Ch <-binary_to_list(W)],
Key = lists:foldl(fun(N, Acc) ->
Acc bor (1 bsl N)
end, 0, List),
case find_key(Key, -1, MapKey) of
{ok, HasKey, MapKey1} ->
Num = maps:get(HasKey, MapNum) + 1,
MapNum1 = maps:update(HasKey, Num, MapNum),
do_init(Words, Seq, MapKey1, MapNum1, Size-1, max(Len, Num));
_ ->
Seq1 = Seq -- List,
{Size1, Num1, MapKey1} = add_char(Seq1, Key, Key, MapKey, MapNum, Size, 1),
{Size2, Num2, MapKey2} = delete_char(List, Seq1, Key, MapKey1, MapNum, Size1, Num1),
MapKey3 = maps:put(Key, Key, MapKey2),
MapNum3 = maps:put(Key, Num2, MapNum),
do_init(Words, Seq, MapKey3, MapNum3, Size2, max(Len, Num2))
end.
find_key(Key, NextKey, MapKey) ->
case maps:find(Key, MapKey) of
{ok, Key} ->
{ok, Key, MapKey};
{ok, Key1} ->
if
NextKey =/= -1 ->
find_key(Key1, NextKey, maps:put(Key, NextKey, MapKey));
true ->
find_key(Key1, NextKey, MapKey)
end;
_ ->
-1
end.
add_char([], AddKey, Key, MapKey, MapNum, Size, Num) ->
{Size, Num, MapKey};
add_char([Ch | L], AddKey, Key, MapKey, MapNum, Size, Num) ->
Key1 = AddKey bor (1 bsl Ch),
case find_key(Key1, Key, MapKey) of
{ok, HasKey, MapKey1} ->
if
HasKey =:= Key ->
add_char(L, AddKey, Key, MapKey1, MapNum, Size, Num);
true ->
MapKey2 = maps:update(HasKey, Key, MapKey1),
HasNum = maps:get(HasKey, MapNum),
add_char(L, AddKey, Key,MapKey2, MapNum, Size -1, Num + HasNum)
end;
_ ->
add_char(L, AddKey, Key, MapKey, MapNum, Size, Num)
end.
delete_char([], Seq, Key, MapKey, MapNum, Size, Num) ->
{Size, Num, MapKey};
delete_char([Ch | L], Seq, Key, MapKey, MapNum, Size, Num) ->
Key1 = Key bxor (1 bsl Ch),
{Size1, Num1, MapKey1} = add_char(Seq, Key1, Key, MapKey, MapNum, Size, Num),
case find_key(Key1, Key, MapKey1) of
{ok, HasKey, MapKey2} ->
if
HasKey =:= Key ->
delete_char(L, Seq, Key, MapKey2, MapNum, Size1, Num1);
true ->
MapKey3 = maps:update(HasKey, Key, MapKey2),
HasNum = maps:get(HasKey, MapNum),
delete_char(L, Seq, Key, MapKey3, MapNum, Size1 -1 , Num1 + HasNum)
end;
_ ->
delete_char(L, Seq, Key, MapKey1, MapNum, Size1, Num1)
end.