Erlang 查找最长子字符串的长度

Erlang 查找最长子字符串的长度,erlang,Erlang,我看到类似的问题,但最终,对于不同的编程语言。我正试图解决这个小问题: 给定一个字符串,查找不带 重复字符。例如,最长的子字符串没有 abcabcbb的重复字母为abc,长度为3。对于 bbbbb最长的子字符串是b,长度为1 我不需要答案,但为什么到目前为止我所做的在第二次迭代中失败了 1> longest_substring:main("abcabcbb"). H: 97, T: "bcabcbb", Sub: [] Is 97 in []? false H: 98, T: "cabcb

我看到类似的问题,但最终,对于不同的编程语言。我正试图解决这个小问题:

给定一个字符串,查找不带 重复字符。例如,最长的子字符串没有
abcabcbb
的重复字母为
abc
,长度为
3
。对于
bbbbb
最长的子字符串是
b
,长度为
1

我不需要答案,但为什么到目前为止我所做的在第二次迭代中失败了

1> longest_substring:main("abcabcbb").
H: 97, T: "bcabcbb", Sub: []
Is 97 in []? false
H: 98, T: "cabcbb", Sub: 97
** exception error: no function clause matching string:chr(97,98,1) (string.erl, line 99)
     in function  longest_substring:process/2 (src/leetcode/algorithms/longest_substring.erl, line 28)
2>
这是源代码:

-module(longest_substring).

-export([main/1]).

-spec main(S :: string()) -> string().

%%%==================================================================
%%% Export
%%%==================================================================
main(S) -> process(S, "").

%%%==================================================================
%%% Internal
%%%==================================================================
process([], Sub) -> string:len(Sub);
process([H | T], Sub) ->
  io:format("H: ~w, T: ~p, Sub: ~p~n", [H, T, Sub]),
  Found = string:chr(Sub, H),
  io:format("Is ~w in ~p? ~p~n", [H, Sub, Found =/= 0]),
  % Don't know how to make this `if` thing better...
  if
    Found > 0 -> process(T, H);
    _ -> process(T, string:concat(Sub, H))
  end.

您有两个地方将字符
H
视为字符串,都在
if
中:

if
    Found > 0 -> process(T, H);
    _ -> process(T, string:concat(Sub, H))
end.
这里的
H
的两种外观都需要是
[H]
,以从单个字符形成字符串。(另外,
if
中的最后一个子句需要使用
true
,而不是下划线-您应该得到一个编译器错误。)

当前,您的解决方案返回一个数字,而不是字符串。它也无法记住任何可能出现在字符串早期的子字符串。要解决这个问题,您需要记住迄今为止看到的最长的子字符串,这意味着您需要另一个累加器:

-module(longest_substring).
-export([main/1]).

-spec main(S :: string()) -> string().

main(S) -> process(S, {0,[]}, {0,[]}).

process([], {LL,Last}, {LG,_}) when LL > LG -> Last;
process([], _, {_,Long}) -> Long;
process([H | T], {LL,Last}=Sub, {LG,_}=Long) ->
    case string:rchr(Last, H) of
        0 ->
            process(T, {LL+1,string:concat(Last,[H])}, Long);
        N ->
            NewLast = {1+LL-N,string:substr(Last,N+1)++[H]},
            process(T, NewLast,
                    case LL > LG of
                        true ->
                            Sub;
                        false ->
                            Long
                    end)
    end.
main/1
功能将两个累加器传递给
process/3
,每个累加器都是一对长度和一个列表。第一个累加器跟踪当前的子字符串,第二个累加器跟踪到目前为止看到的最长的子字符串

process/3
的最后一句中,我们首先检查
H
是否在当前子字符串中;如果没有,我们将其添加到当前子字符串中,将其长度增加1,然后使用字符串的尾部再次调用
process/3
。但是,如果在当前子字符串中找到
H
,我们将使用
string:rchr/2
的返回值计算新的当前子字符串,以保留我们可以保留的前一子字符串的最长部分(原始解决方案不这样做)。然后检查当前子字符串的长度是否大于当前最长的子字符串,如果大于,则将其设为最长的子字符串,否则将其丢弃并保留当前最长的子字符串,然后继续使用字符串的尾部。(请注意,我们也可以检查是否大于或等于,而不是大于;这将使我们的函数返回找到的最后一个最长的子字符串,而不是第一个。)


process/3
的前两个子句处理输入字符串已被完全处理的情况。它们只是决定当前子字符串是否比迄今为止看到的最长子字符串长,然后返回两个子字符串中的较长者。(这里也可以使用更大或相等的比较。)

您有两个地方将字符
H
视为字符串,这两个地方都在
if
中:

if
    Found > 0 -> process(T, H);
    _ -> process(T, string:concat(Sub, H))
end.
这里的
H
的两种外观都需要是
[H]
,以从单个字符形成字符串。(另外,
if
中的最后一个子句需要使用
true
,而不是下划线-您应该得到一个编译器错误。)

当前,您的解决方案返回一个数字,而不是字符串。它也无法记住任何可能出现在字符串早期的子字符串。要解决这个问题,您需要记住迄今为止看到的最长的子字符串,这意味着您需要另一个累加器:

-module(longest_substring).
-export([main/1]).

-spec main(S :: string()) -> string().

main(S) -> process(S, {0,[]}, {0,[]}).

process([], {LL,Last}, {LG,_}) when LL > LG -> Last;
process([], _, {_,Long}) -> Long;
process([H | T], {LL,Last}=Sub, {LG,_}=Long) ->
    case string:rchr(Last, H) of
        0 ->
            process(T, {LL+1,string:concat(Last,[H])}, Long);
        N ->
            NewLast = {1+LL-N,string:substr(Last,N+1)++[H]},
            process(T, NewLast,
                    case LL > LG of
                        true ->
                            Sub;
                        false ->
                            Long
                    end)
    end.
main/1
功能将两个累加器传递给
process/3
,每个累加器都是一对长度和一个列表。第一个累加器跟踪当前的子字符串,第二个累加器跟踪到目前为止看到的最长的子字符串

process/3
的最后一句中,我们首先检查
H
是否在当前子字符串中;如果没有,我们将其添加到当前子字符串中,将其长度增加1,然后使用字符串的尾部再次调用
process/3
。但是,如果在当前子字符串中找到
H
,我们将使用
string:rchr/2
的返回值计算新的当前子字符串,以保留我们可以保留的前一子字符串的最长部分(原始解决方案不这样做)。然后检查当前子字符串的长度是否大于当前最长的子字符串,如果大于,则将其设为最长的子字符串,否则将其丢弃并保留当前最长的子字符串,然后继续使用字符串的尾部。(请注意,我们也可以检查是否大于或等于,而不是大于;这将使我们的函数返回找到的最后一个最长的子字符串,而不是第一个。)


process/3
的前两个子句处理输入字符串已被完全处理的情况。它们只是决定当前子字符串是否比迄今为止看到的最长子字符串长,然后返回两个子字符串中的较长者。(这里也可以使用更大或相等的比较。)

为了好玩,我建议您避免复杂的搜索。在这个解决方案中,我为列表中的每个元素创建了一个进程:元素本身、列表中下一个进程/元素的Pid以及调用者的Pid

为了启动搜索,我向每个进程/元素发送一个空列表

每次流程/元素接收到列表时,它都会检查其存储的元素是否是所接收列表的成员。如果是,则将列表发送回调用者,如果不是,则将元素前置到列表,并将新列表发送到下一个进程/元素以继续评估

调用方进程只需等待发送的返回消息的数量

我为列表的最后一个元素添加了一条停止消息和一个特例

-module (longer).

-compile([export_all]).

char_proc(V,Next,Caller) ->
    receive
        stop -> ok;
        Str ->
            case lists:member(V,Str) of
                true -> Caller ! Str;
                false -> send(Next,Caller,[V|Str])
            end,
            char_proc(V,Next,Caller)
    end.

send(noproc,Caller,Str) -> Caller ! Str;
send(Next,_,Str) -> Next ! Str.

find(Str) ->
    Me = self(),
    Pids = tl(lists:reverse(lists:foldl(fun(X,Acc) -> Pid = spawn(?MODULE,char_proc,[X,hd(Acc),Me]), [Pid|Acc] end,[noproc],Str))),
    [X ! [] || X <- Pids],
    R = receive_loop(0,[],length(Str)),
    [X ! stop || X <- Pids],
    R.

receive_loop(N,S,0) -> {N,S};
receive_loop(N,S,I) ->
    receive
        M when length(M) > N ->
            receive_loop(length(M),M,I-1);
        _ ->
            receive_loop(N,S,I-1)
    end.
注意如果存在多个解决方案,则无法保证将返回witch子字符串,修改代码以返回第一个子字符串或