erlang算法系列(并查集)-leetclode-2157. 字符串分组(困难)

leetclode-2157. 字符串分组-原题

给你一个下标从 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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值