代码之家  ›  专栏  ›  技术社区  ›  IVlad

如何计算将字符串转换为回文所需的字符数?

  •  18
  • IVlad  · 技术社区  · 16 年前

    我最近发现了一个竞赛问题,它要求您计算字符串中必须插入(任何位置)的最小字符数,以便将其转换为回文。

    例如,给定字符串:“abcbd”,我们只需插入两个字符即可将其转换为回文:一个在“a”之后,另一个在“d”之后:“a” bcbd公司 ".

    这似乎是一个类似问题的推广,该问题要求相同的东西,除了字符只能添加在末尾-这在O(N)中有一个使用哈希表的非常简单的解决方案。

    Levenshtein distance algorithm 解决这个问题,但一直没有成功。任何关于如何解决这个问题的帮助(不一定要有效率,我只是对任何DP解决方案感兴趣)都将不胜感激。

    3 回复  |  直到 16 年前
        1
  •  7
  •   Aryabhatta Aryabhatta    16 年前

    当然,如果您决定更改允许的操作,这种“天真”的算法实际上可能会派上用场。


    给定一个字符串,我们猜测得到的回文的中间,然后尝试计算使该字符串成为围绕该中间的回文所需的插入次数。

    假设我们考虑一个中间,它给出了两个字符串L和R(一对左和一对右)。

    Longest Common Subsequence 算法(这是一个DP算法)现在可以用来创建一个“super”字符串,其中包含L和R的倒数,请参阅 Shortest common supersequence

    选择中间的插入数最少的部分。

    我想这是O(n^3)(注意:我没有试着证明这是真的)。

        2
  •  2
  •   user2023861    12 年前

    我的C#解决方案查找字符串中的重复字符,并使用它们减少插入的次数。用这样的话来说 程序 ,我使用“r”字符作为边界。在r的内部,我将其作为回文(递归)。在r的外面,我镜像了左右两边的人物。

    某些输入有多个最短输出: 输出 吹牛 奥图普托 . 我的解决方案只选择了其中一种可能性。

    一些示例运行:

    • 雷达 -&燃气轮机; 雷达
    • 电子系统 气象系统 ,2个插入
    • 消息 -&燃气轮机;
    • 堆栈交换 -&燃气轮机; ,8个插入

    首先,我需要检查输入是否已经是回文:

    public static bool IsPalindrome(string str)
    {
        for (int left = 0, right = str.Length - 1; left < right; left++, right--)
        {
            if (str[left] != str[right])
                return false;
        }
        return true;
    }
    

    然后我需要在输入中找到任何重复的字符。可能不止一个。这个词 有两个重复最多的字符(“e”和“s”):

    private static bool TryFindMostRepeatedChar(string str, out List<char> chs)
    {
        chs = new List<char>();
        int maxCount = 1;
    
        var dict = new Dictionary<char, int>();
        foreach (var item in str)
        {
            int temp;
            if (dict.TryGetValue(item, out temp))
            {
                dict[item] = temp + 1;
                maxCount = temp + 1;
            }
            else
                dict.Add(item, 1);
        }
    
        foreach (var item in dict)
        {
            if (item.Value == maxCount)
                chs.Add(item.Key);
        }
    
        return maxCount > 1;
    }
    

    我的算法如下:

    public static string MakePalindrome(string str)
    {
        List<char> repeatedList;
        if (string.IsNullOrWhiteSpace(str) || IsPalindrome(str))
        {
            return str;
        }
        //If an input has repeated characters,
        //  use them to reduce the number of insertions
        else if (TryFindMostRepeatedChar(str, out repeatedList))
        {
            string shortestResult = null;
            foreach (var ch in repeatedList) //"program" -> { 'r' }
            {
                //find boundaries
                int iLeft = str.IndexOf(ch); // "program" -> 1
                int iRight = str.LastIndexOf(ch); // "program" -> 4
    
                //make a palindrome of the inside chars
                string inside = str.Substring(iLeft + 1, iRight - iLeft - 1); // "program" -> "og"
                string insidePal = MakePalindrome(inside); // "og" -> "ogo"
    
                string right = str.Substring(iRight + 1); // "program" -> "am"
                string rightRev = Reverse(right); // "program" -> "ma"
    
                string left = str.Substring(0, iLeft); // "program" -> "p"
                string leftRev = Reverse(left); // "p" -> "p"
    
                //Shave off extra chars in rightRev and leftRev
                //  When input = "message", this loop converts "meegassageem" to "megassagem",
                //    ("ee" to "e"), as long as the extra 'e' is an inserted char
                while (left.Length > 0 && rightRev.Length > 0 && 
                    left[left.Length - 1] == rightRev[0])
                {
                    rightRev = rightRev.Substring(1);
                    leftRev = leftRev.Substring(1);
                }
    
                //piece together the result
                string result = left + rightRev + ch + insidePal + ch + right + leftRev;
    
                //find the shortest result for inputs that have multiple repeated characters
                if (shortestResult == null || result.Length < shortestResult.Length)
                    shortestResult = result;
            }
    
            return shortestResult;
        }
        else
        {
            //For inputs that have no repeated characters, 
            //  just mirror the characters using the last character as the pivot.
            for (int i = str.Length - 2; i >= 0; i--)
            {
                str += str[i];
            }
            return str;
        }
    }
    

    请注意,您需要一个反向函数:

    public static string Reverse(string str)
    {
        string result = "";
        for (int i = str.Length - 1; i >= 0; i--)
        {
            result += str[i];
        }
        return result;
    }
    
        3
  •  1
  •   Ernesto Cejas    13 年前

    递归解决方案添加到字符串末尾:

    有两个基本情况。当长度为1或2时。递归情况:如果极值相等,则 使回文成为不带极端的内部字符串,并用极端返回该字符串。 如果两个极端不相等,则将第一个字符添加到末尾,并将回文设置为 包含上一个最后一个字符的内部字符串。把那个还给我。

    public static string ConvertToPalindrome(string str) // By only adding characters at the end
        {
            if (str.Length == 1) return str; // base case 1
            if (str.Length == 2 && str[0] == str[1]) return str; // base case 2
            else
            {
                if (str[0] == str[str.Length - 1]) // keep the extremes and call                
                    return str[0] + ConvertToPalindrome(str.Substring(1, str.Length - 2)) + str[str.Length - 1];
                else //Add the first character at the end and call
                    return str[0] + ConvertToPalindrome(str.Substring(1, str.Length - 1)) + str[0];
            }
        }