找出我们是否能得到回文

时间:2014-11-07 13:56:03

标签: c++ algorithm

给出一个字符串S.我们需要告诉我们是否可以通过从中删除一个字母来使其成为回文。 我有一个O(N ^ 2)方法,通过修改编辑距离方法。他们有更好的方法吗?

我的方法:

int ModifiedEditDistance(const string& a, const string& b, int k) {
    int i, j, n = a.size();
    int dp[MAX][MAX];
    memset(dp, 0x3f, sizeof dp);    
    for (i = 0 ; i < n; i++)
        dp[i][0] = dp[0][i] = i;

    for (i = 1; i <= n; i++) {
        int from = max(1, i-k), to = min(i+k, n);
        for (j = from; j <= to; j++) {
            if (a[i-1] == b[j-1])           // same character
                dp[i][j] = dp[i-1][j-1];    
            // note that we don't allow letter substitutions

            dp[i][j] = min(dp[i][j], 1 + dp[i][j-1]); // delete character j
            dp[i][j] = min(dp[i][j], 1 + dp[i-1][j]); // insert character i
        }
    }
    return dp[n][n];
}

如何提高空间复杂度,因为字符串的最大大小可以达到10 ^ 5.

请帮忙。

示例:让String为abc然后回答是&#34; NO&#34;如果字符串是&#34; abbcbba,那么答案是&#34;是&#34;

9 个答案:

答案 0 :(得分:9)

关键的观察是,如果第一个和最后一个字符是相同的,那么你不需要删除其中任何一个;也就是说xSTRINGx可以通过删除单个字母变成回文,当且仅当STRING可以(只要STRING至少有一个字符长)。

你想定义一个方法(借口Java语法 - 我不是C ++编码器):

boolean canMakePalindrome(String s, int startIndex, int endIndex, int toRemove);

通过删除startIndex字符来确定从endIndex-1toRemove的字符串部分是否可以成为回文结构。

当您考虑canMakePalindrome(s, i, j, r)时,您可以根据较小的问题来定义它:

  1. 如果j-i为1,则返回true;如果它为0则返回true当且且仅当r为0.这里的要点是1个字符的字符串是回文,无论你是否删除了一个字符;一个0长度的字符串是一个回文,但不能通过删除一个字符组成一个(因为没有任何要删除)。
  2. 如果s[i]s[j-1]相同,则答案与canMakePalindrome(s, i+1, j-1, r)相同。
  3. 如果它们不同,则s[i]s[j-1]需要删除。如果toRemove为零,则返回false,因为您还没有要删除任何字符。如果toRemove为1,则如果canMakePalindrome(s, i+1, j, 0)canMakePalindrome(s, i, j-1, 0),则返回true。这是因为如果你删除这两个字符中的一个,你现在正在测试它是否已经是一个回文。
  4. 现在,我认为这很容易编码。

    如果您想允许删除多个字符,您可以使用相同的想法,但使用动态编程。只需移除一个字符,动态编程将减少常数因子,但不会降低渐近时间复杂度(字符串长度的线性)。

答案 1 :(得分:4)

Psudocode(像这样的东西我根本没有测试过它。)

它基于检测您可以移除角色的条件,即

  1. 正好有一个错误的字符
  2. 是一种综合征(0不匹配)
  3. O(n)及时,O(1)在空间。

     bool foo(const std::string& s)
     {
       int i = 0;
       int j = s.size()-1;
       int mismatch_count = 0;
       while (i < j)
       {
           if (s[i]==s[j])
           {
              i++; j--;
           }
           else
           {
              mismatch_count++;
              if (mismatch_count > 1) break;
              //override first preference if cannot find match for next character
              if (s[i+1] == s[j] && ((i+2 >= j-1)||s[i+2]==s[j-1]))
              {
                 i++;
              }
              else if (s[j-1]==s[i])
              {
                 j--;
              }
              else
              {
                 mismatch_count++; break;
              }
           }
        }  
        //can only be a palendrome if you remove a character if there is exactly one mismatch
        //or if a palendrome
        return (mismatch_count == 1) || (mismatch_count == 0);
     }
    

答案 2 :(得分:2)

这是一个(稍微不完整)的解决方案,需要O(n)时间和O(1)空间。

// returns index to remove to make a palindrome; string::npos if not possible
size_t willYouBeMyPal(const string& str)
{
  size_t toRemove = string::npos;
  size_t len = str.length();

  for (size_t c1 = 0, c2 = len - 1; c1 < c2; ++c1, --c2) {
    if (str[c1] != str[c2]) {
      if (toRemove != string::npos) {
        return string::npos;
      }

      bool canRemove1 = str[c1 + 1] == str[c2];
      bool canRemove2 = str[c1] == str[c2 - 1];
      if (canRemove1 && canRemove2) {
        abort(); // TODO: handle the case where both conditions are true
      } else if (canRemove1) {
        toRemove = c1++;
      } else if (canRemove2) {
        toRemove = c2--;
      } else {
        return string::npos;
      }
    }
  }

  // if str is a palindrome already, remove the middle char and it still is
  if (toRemove == string::npos) {
    toRemove = len / 2;
  }

  return toRemove;
}

如果你得到这个,练习就是做什么:

abxyxcxyba

正确的解决方案是:

ab_yxcxyba

但是你可能会走上一条糟糕的道路:

abxyxcx_ba

所以当你找到&#34; next&#34;双方的角色是一个可能的解决方案,你需要评估两种可能性。

答案 3 :(得分:2)

我写了一个O(n)复杂度的样本,适用于我投入的测试。虽然不多:D 它背后的想法是忽略它们相同的第一个和最后一个字母,如果它们不相同则删除它们中的一个,并推断当字符串足够小时会发生什么。相同的结果可以使用循环而不是递归进行存档,这将节省一些空间(使其成为O(1)),但它更难以理解并且更容易出错IMO。

bool palindrome_by_1(const string& word, int start, int end, bool removed = false)  // Start includes, end excludes
{
    if (end - start == 2){
        if (!removed)
            return true;

        return word[start] == word[end - 1];
    }

    if (end - start == 1)
        return true;


    if (word[start] == word[end - 1])
        return palindrome_by_1(word, start + 1, end - 1, removed);

    // After this point we need to remove a letter
    if (removed) 
        return false;

    // When two letters don't match, try to eliminate one of them
    return palindrome_by_1(word, start + 1, end, true) || palindrome_by_1(word, start, end - 1, true);

}

答案 4 :(得分:1)

检查单个字符串是否是回文结构是O(n)。你可以实现一个类似的算法,而不是移动两个指针,一个从开始,另一个从结束。只要字符相同,移动每个指针,并在第一个不匹配时尝试匹配您可以跳过的字符,并且只要其余字符相同,就继续移动两个指针。跟踪第一个不匹配。这是O(n)。

答案 5 :(得分:1)

我希望我的算法在没有提供代码的情况下通过。

如果一个单词a 1 a 2 .... a n 可以通过删除 k <做成回文/ sub>,我们可以搜索k如下:

如果 1 != a n ,那么唯一可能的k将是1或n。只需检查 1 a 2 .... a n-1 2 a 3 .... a n 是回文。

如果 1 == a n ,下一步就是为 2 .... a 解决同样的问题n-1个。所以我们在这里有一个递归。

答案 6 :(得分:0)

public static boolean pal(String s,int start,int end){

    if(end-start==1||end==start)
        return true;

    if(s.charAt(start)==s.charAt(end))
        return pal(s.substring(start+1, end),0,end-2);

    else{

        StringBuilder sb=new StringBuilder(s);
        sb.deleteCharAt(start);
        String x=new String(sb);
        if(x.equals(sb.reverse().toString()))
            return true;

        StringBuilder sb2=new StringBuilder(s);
        sb2.deleteCharAt(end);
        String x2=new String(sb2);
        if(x2.equals(sb2.reverse().toString()))
            return true;


    }

    return false;

}

答案 7 :(得分:0)

我尝试了以下内容,f和b是字符不匹配的索引

int canwemakepal(char *str)//str input string
{
    long int f,b,len,i,j;
   int retval=0;
   len=strlen(str);
    f=0;b=len-1;
    while(str[f]==str[b] && f<b)//continue matching till we dont get a mismatch
    {
        f++;b--;
    }
    if(f>=b)//if the index variable cross over each other, str is palindrome,answer is yes
    {
        retval=1;//true
    }
    else if(str[f+1]==str[b])//we get a mismatch,so check if removing character at str[f] will give us a palindrome
    {
        i=f+2;j=b-1;
        while(str[i]==str[j] && i<j)
        {
            i++;j--;
        }
        if(i>=j)
        retval=1;
        else
        retval=0;
    }
    else if(str[f]==str[b-1])//else check the same for str[b]
    {
        i=f+1;j=b-2;
        while(str[i]==str[j] && i<j)
        {
            i++;j--;
        }
        if(i>=j)
        retval=1;
        else
        retval=0;
    }
    else 
    retval=0;

    return retval;

}

答案 8 :(得分:0)

I created this solution,i tried with various input giving correct result,still not accepted as correct solution,Check it n let me know if m doing anything wrong!! Thanks in advance.
public static void main(String[] args) 
{

    Scanner s = new Scanner(System.in);
    int t = s.nextInt();
    String result[] = new String[t];
    short i = 0;
    while(i < t)
    {
        String str1 = s.next();
        int length  = str1.length();
        String str2 = reverseString(str1);
        if(str1.equals(str2))
        {
                result[i] = "Yes";
        }
        else 
        {
                if(length == 2)
                {
                    result[i] = "Yes";
                }
                else
                {
                    int x = 0,y = length-1;

                    int counter = 0;
                    while(x<y)
                    {
                        if(str1.charAt(x) == str1.charAt(y))
                        {
                            x++;
                            y--;
                        }
                        else
                        {
                            counter ++;
                            if(str1.charAt(x) == str1.charAt(y-1))
                            {
                                y--;
                            }
                            else if(str1.charAt(x+1) == str1.charAt(y))
                            {
                                x++;
                            }
                            else
                            {
                                counter ++;
                                break;
                            }
                        }

                    }
                    if(counter >= 2)
                    {
                        result[i] = "No";
                    }
                    else
                        result[i]="Yes";
                }

        }
        i++;

    } // Loop over

    for(int j=0; j<i;j++)
    {
        System.out.println(result[j]);
    }
}

public static String reverseString(String original)
{
    int length = original.length();
    String reverse = "";
     for ( int i = length - 1 ; i >= 0 ; i-- )
         reverse = reverse + original.charAt(i);
    return reverse;  
}