我的算法中缺少什么角落来找到最多K位替换的最大回文数?

时间:2016-08-10 04:56:50

标签: c# string algorithm time-complexity dynamic-programming

我已经在这方面工作了好几个小时,我的解决方案是满足性能基准测试,并且几乎通过了所有测试用例。它是O(n),在评论中有很好的描述。不幸的是,测试用例非常庞大,以至于无法通过它们,所以我希望你们中的一个能够提供一副清新的眼睛,看看我是否可能错过一个角落的情况这里。

static string LargestPalindromeAfterKReplacements(string number, int k)
{
    // Finds the largest palindromic number (digits are the same in reverse order)
    // that can be formed by at most k replacements of digits.
    // e.g. "001", k=2 --> "909"
    //      "001", k=1 --> "101"
    //      "001", k=0 --> "-1" (not possible)

    // Get number as a StringBuilder element
    var sb = new StringBuilder(number);

    // For the first pass through the string, replace any of the unequal
    // characters on the left and right side by the larger of the two. For
    // later use, keep track of the left index of each of these replacements.
    var replacementIndices = new Queue<int>(); 
    for(int i = 0, j = sb.Length - 1; i < j && k > 0; ++i, --j)
    {
        if(sb[i] < sb[j])
        {
            sb[i] = sb[j];
            replacementIndices.Enqueue(i);
            k -= 1;
        }
        else if(sb[i] > sb[j])
        {
            sb[j] = sb[i];
            replacementIndices.Enqueue(i);
            k -= 1;
        }
    } 

    // If sb isn't a palindrome at this point, then it was never possible to
    // make it one. 
    if(!IsPalindrome(sb.ToString()))
        return "-1";

    // If here, sb is a palindrome. If we have any k left over, then for any of
    // the indices where we made a replacement, if the pair isn't already both 9, 
    // we coudld've made them both 9 during the first pass through the string, at
    // a cost decreasing k by 2 rather than by 1. "Replay" the original pass like this.
    while(k > 0 && replacementIndices.Count > 0) 
    {
        int i = replacementIndices.Dequeue(), j = sb.Length - i - 1;
        if(sb[i] != '9')
        {
            sb[i] = '9';
            sb[j] = '9';
            k -= 1;
        }
    }

    // In case we still have k > 0, that means we ran out of replacementIndices.
    // Make a third pass through the string and make any non-equal characters
    // on opposite ends become 9. 
    for(int i = 0, j = sb.Length - 1; i <= j && k > 0; ++i, --j)
    {
        if(sb[i] != '9')
        {
            if(k > 1)
            {
              sb[i] = '9';
              sb[j] = '9'; 
              k -= 2;
            }
            else if(i == j) // k = 1 and i = j
            {
                sb[i] = '9';
            }
        }
    }

    // In case we ran out of replacement indices, 
    return sb.ToString();
}

1 个答案:

答案 0 :(得分:2)

Abishek Bansal的评论是正确的:例如,如果sb =“1234561”和k = 4,你将首先使用2个子位移动到“1654561”(到目前为止是正确的) - 但是从那里到“1994991” “而不是”9654569“,因为您的while循环更喜欢便宜的更改以更好地进行更改。

但还有第二个错误:为什么,在你的最后一个循环中,如果k == 1,你是否只尝试修改中间(i == j)数字?这意味着,例如,对于输入sb =“121”和k = 4,当可能清楚地“999”时,您的算法将返回“929”。 (对于类似的反例,但k <= n,考虑输入sb =“929”和k = 2.)

最后:调试这样的代码的最简单方法是生成低于特定大小的所有可能实例(或至少其中很多)并将算法的输出与a的输出进行比较每个都有一个已知良好的(例如蛮力)算法,一旦你用不同的输出击中输入就停止。这将(通常)给你一个小到足以手动分析的例子。