计算XYYX型的回文

时间:2016-07-23 18:34:12

标签: string algorithm optimization

以下是面试问题。我没有做到这一点,因为我没有预料到的更好,更有效的答案,我没有。

给定一个字符串S,我们需要找到模式XYYX的回文数,其中X和Y是同一个字符串S中的任何字符。

注意:我们不能改变字符串S中字符的顺序.X和Y的值也可以相同,但它们在字符串S中的位置需要不同。

此外,如果字符串S中的索引对于最终字符串

中的任何字符不同,则两个字符串不同

示例:Say string是S = abbaab,然后是4。

四个这样的回文是:

//In Java.
exposeObject(myObject, "nameOfMyObject");

--Then in Lua
nameOfMyObject:myFunction();

那么如何计算这些回文的数量。字符串的长度可以达到1000000.那么最好的方法是什么呢?

1 个答案:

答案 0 :(得分:0)

如问题的讨论部分所示,目标不是找到真实的"回文但像是" XYYX *"每个" X *"匹配计算。

这意味着您需要在迭代输入S时保持一些搜索状态,就像S的每个位置一样,可能有多个回文。

现在考虑" abbaaaa的(最坏情况?)输入...." (以" abba&#34开头;以(1000000 - 4)' a' -characters结尾。在这种情况下,我们需要维护多少个状态实例?答案取决于如何定义了状态机(维护状态)。

提到的状态机是比赛的检测器。让我们看一些代码(选择F#):

type PState =
  | X of char
  | Y0 of char * char
  | Y1 of char * char
  | X1 of char * char
  | XN of char * char
  | End

let tryProgress c = function
    | X(x) -> Some(Y0(x,c))
    | Y0(x,y) -> if c = y then Some(Y1(x,y)) else None
    | Y1(x,y) -> if c = x then Some(X1(x,y)) else None
    | X1(x,y) -> if c = x then Some(XN(x,y)) else Some (End)
    | XN(x,y) -> if c = x then Some(XN(x,y)) else Some (End)
    | End -> failwith "This instance is done and should have been dismissed."

let count (s : string) =
    s.ToCharArray()
    |> Array.fold (fun (states,counter) c ->
            let counter1 = ref 0
            (X(c)) :: (states 
            |> List.map (fun fsm ->
                    let res = tryProgress c fsm
                    // printfn "%A -> %c -> %A" fsm c res
                    match res with
                    | None -> None
                    | Some(X1(x,y)) ->
                        counter1 := !counter1 + 1
                        Some(X1(x,y))
                    | Some(XN(x,y)) -> 
                        counter1 := !counter1 + 1
                        Some(XN(x,y))
                    | Some(End) -> 
                        counter1 := !counter1 + 1
                        None
                    | Some(fsm1) -> Some(fsm1)
                )
            |> List.filter (fun mfsm -> match mfsm with | Some(fsm) -> true | None -> false)
            |> List.map (fun mfsm -> Option.get mfsm))
            , (!counter1 + counter)
        ) ([],0)
    |> snd

let s = "abbaab"
count s

(*
    Yields: 
    val it : int = 4
    as hoped for.
*)

类型PState是一个有区别的联合,它描述了状态机的各个状态以及各个状态所需的数据。

tryProgress函数从S获取fsm的实例和下一个输入字符并返回Pstate option结果。如果字符没有导致新的可能状态,则此函数返回None,否则返回Some <resulting state>

最后,count函数迭代输入字符串中的字符(也可以按字母顺序读取输入文件...)并将tryProgress函数应用于每个活动状态机。它也决定了哪些情况会忘记&#34;并维持和决定何时增加发现的伪回文的计数器。

回到我们所谓的最坏情况输入场景,保留的FSM实例数将随着字符数的增加而增加,X = Y也无助于减少要保留的状态数。大多数这些实例将处于状态XN('a','a'),直到序列结束。

因此,我很想把这个问题归为一个不合适的问题。 ("aaaaaaa" -> [0,1,2,3;0,1,2,4;0,1,2,5;0,1,2,6; 1,2,3,4; 1,2,3,5; 1,2,3,6; ...])。

<强>更新

由于代码示例需要另一种语言,这里是流行的C ++版本:

#include <cstdint>
#include <string>
#include <list>
#include <iostream>
#include <exception>
#include <stdexcept>

enum class PStateName
{
    X,Y0,Y1,X1,XN,END,FAIL
};

struct PState 
{
    PStateName state;
    char x;
    char y;

    PState(PStateName s, char x0, char y0 = '\0')
    : state(s)
    , x(x0)
    , y(y0)
    {

    }

    static PState MakeX(char x0)
    {
        return PState(PStateName::X, x0);
    }
    static PState To_Y0(const PState &pre, char y0)
    {
        return PState(PStateName::Y0,pre.x, y0);
    }
    static PState To_Y1(const PState&pre)
    {
        return PState(PStateName::Y1,pre.x,pre.y);
    }
    static PState To_X1(const PState&pre)
    {
        return PState(PStateName::X1,pre.x,pre.y);
    }
    static PState To_XN(const PState&pre)
    {
        return PState(PStateName::XN,pre.x,pre.y);
    }
    static PState To_End(const PState&pre)
    {
        return PState(PStateName::END,pre.x,pre.y);
    }
    static PState To_Fail(const PState&pre)
    {
        return PState(PStateName::FAIL,pre.x,pre.y);
    }
};

PState Progress(const PState& current, char c)
{
    switch(current.state)
    {
        case PStateName::X:
            return PState::To_Y0(current,c);
            break;
        case PStateName::Y0:
            if(c == current.y)
                return PState::To_Y1(current);
            else
                return PState::To_Fail(current);
            break;
        case PStateName::Y1:
            if(c == current.x)
                return PState::To_X1(current);
            else
                return PState::To_Fail(current);
            break;
        case PStateName::X1:
            if(c == current.x)
                return PState::To_XN(current);
            else
                return PState::To_End(current);
            break;
        case PStateName::XN:
            if(c == current.x)
                return PState::To_XN(current);
            else
                return PState::To_End(current);
            break;
        case PStateName::END:
            throw std::exception(/*"Instances in state End should have been dismissed and never show here."*/);
            break;
        case PStateName::FAIL:
            throw std::exception(/*"Instances in state FAIL should have been dismissed and never show here."*/);
            break;
        default:
            throw std::exception(/*"Fix me! Unknown state in a switch statement!"*/);
            break;
    }
}

typedef std::list<PState> StateList;

StateList nextChar( const StateList& actives, char c, size_t &counter )
{
    StateList result;
    for(auto fsm : actives)
    {
        PState fsm1 = Progress(fsm,c);
        switch(fsm1.state)
        {
            case PStateName::FAIL: // Do nothing - weed out and do not count it.
                break;
            case PStateName::X1:
                counter++;
                result.push_back(fsm1);
                break;
            case PStateName::XN:
                counter++;
                result.push_back(fsm1);
                break;
            case PStateName::END:
                counter++;
                break;
            default:
                result.push_back(fsm1);
                break;            
        }
    }

    result.push_back(PState::MakeX(c));
    return result;
}

size_t countString( const std::string& s)
{
    size_t counter = 0;
    StateList current;
    for(auto c : s)
    {
        current = nextChar(current,c,counter);
    }
    return counter;
}

size_t countFile( const std::string& filePath)
{
    size_t counter = 0;
    StateList current;
    // TODO: read file char by char and feed it to nextChar as shown in the countString() function.
    return counter;
}


int main(int argc, const char * argv[])
{
    std::string s("abbaab");
    std::cout <<"\"" <<  s << "\": " << countString(s) << std::endl; 
    return 0;
}