将字符串转换为回文的最小交换次数

时间:2018-08-11 03:35:24

标签: string algorithm

我们得到一个字符串,我们必须找出将其转换为回文的最小交换次数。

Ex-
Given string: ntiin
Palindrome: nitin
Minimum number of swaps: 1

如果无法将其转换为回文,请返回-1

brute force外,我无法想到其他任何方法。我们可以检查第一个和最后一个字符,如果它们相等,则检查较小的子字符串,然后对其施加蛮力。但这将具有很高的复杂性,我认为可以以另一种方式解决这个问题。也许dynamic programming。如何处理?

4 个答案:

答案 0 :(得分:3)

有与所问相同的问题! https://www.codechef.com/problems/ENCD12

我得到了这个解决方案的交流 https://www.ideone.com/8wF9DT

//minimum adjacent swaps to make a string to its palindrome
#include<bits/stdc++.h>
using namespace std;
bool check(string s)
{
    int n=s.length();
    map<char,int> m;
    for(auto i:s)
    {
        m[i]++;
    }
    int cnt=0;
    for(auto i=m.begin();i!=m.end();i++)
    {
        if(i->second%2)
        {
            cnt++;
        }
    }
    if(n%2&&cnt==1){return true;}
    if(!(n%2)&&cnt==0){return true;}
    return false;
}

int main()
{
    string a;
    while(cin>>a)
    {
        if(a[0]=='0')
        {
            break;
        }
        string s;s=a;
        int n=s.length();
        //first check if
        int cnt=0;
        bool ini=false;
        if(n%2){ini=true;}
        if(check(s))
        {
            for(int i=0;i<n/2;i++)
            {
                bool fl=false;
                int j=0;
                for(j=n-1-i;j>i;j--)
                {

                    if(s[j]==s[i])
                    {
                        fl=true;
                        for(int k=j;k<n-1-i;k++)
                        {
                            swap(s[k],s[k+1]);
                            cnt++;
//                            cout<<cnt<<endl<<flush;
                        }
//                        cout<<" "<<i<<" "<<cnt<<endl<<flush;
                        break;
                    }
                }
                if(!fl&&ini)
                {
                    for(int k=i;k<n/2;k++)
                    {
                        swap(s[k],s[k+1]);
                        cnt++;

                    }
//                    cout<<cnt<<" "<<i<<" "<<endl<<flush;
                }
            }
            cout<<cnt<<endl;
        }
        else{
            cout<<"Impossible"<<endl;
        }
    }

}

希望有帮助!

我的代码背后的技术是贪婪的 首先检查回文字符串是否可以存在,以及是否可以 会有两种情况,一种是字符串长度为奇数,然后仅一个字符的计数为奇数 即使那没有计数应该是奇数 然后 从索引0到n / 2-1执行以下操作 修复此字符并从n-i-1到i + 1搜索此字符 如果找到,则从该位置(让说j)交换到其新位置n-i-1 如果字符串长度是奇数,则每次遇到其他字符都没有的字符时,将其移至第n / 2个位置。

答案 1 :(得分:3)

我的解决方案围绕着第一个元素和最后一个元素应该匹配的回文属性,如果它们的相邻元素也不匹配,那么它就不是回文。不断进行比较和交换,直到两个元素都到达同一元素或相邻元素。

Java中的书面解决方案如下:

public static void main(String args[]){
        String input = "natinat";
        char[] arr = input.toCharArray();
        int swap = 0;
        int i = 0;
        int j = arr.length-1;
        char temp;
        while(i<j){
            if(arr[i] != arr[j]){
                if(arr[i+1] == arr[j]){
                    //swap i and i+1 and increment i, decrement j, swap++
                    temp = arr[i];
                    arr[i] = arr[i+1];
                    arr[i+1] = temp;

                    i++;j--;
                    swap++;
                } else if(arr[i] == arr[j-1]){
                    //swap j and j-1 and increment i, decrement j, swap++
                    temp = arr[j];
                    arr[j] = arr[j-1];
                    arr[j-1] = temp;

                    i++;j--;
                    swap++;
                } else if(arr[i+1] == arr[j-1] && i+1 != j-1){
                    //swap i and i+1, swap j and j-1 and increment i, decrement j, swap+2
                    temp = arr[j];
                    arr[j] = arr[j-1];
                    arr[j-1] = temp;

                    temp = arr[i];
                    arr[i] = arr[i+1];
                    arr[i+1] = temp;

                    i++;j--;
                    swap = swap+2;
                }else{
                    swap = -1;break;
                }       
            } else{
                //increment i, decrement j
                i++;j--;
            }
        }       
        System.out.println("No Of Swaps: "+swap);
    }

答案 2 :(得分:2)

首先,您可以检查字符串是否可以转换为回文。

只有一个字母数组(如果所有字母均为小写,则为26个字符),并计算输入字符串中每个字母的数量。

如果字符串长度为偶数,则所有字母数均应为偶数。
如果字符串长度为奇数,则除1以外,所有字母计数均应为奇数。

O(n)中的第一次通过将已经治疗所有-1种情况。

如果字符串长度为奇数,则从将具有奇数的元素移到中间开始。

然后您可以应用以下过程:

使用长度为N的输入字符串S的以下逻辑构建加权图:

对于从索引0到N / 2-1的每个元素:
-如果对称元素S [N-index-1]相同,则继续
-如果不同,则在2个字符(字母顺序)之间创建边,或增加现有字符的权重

这个想法是,当权重相等时,您可以通过一次交换形成两对来进行“良好交换”。 当权重为奇数时,您不能将两个货币对放在一个交换中,交换需要形成一个循环

1. For instance "a    b   a   b"
One edge between a,b of weight 2:
a - b (2)

Return 1

2. For instance: "a     b    c   b    a    c"
a - c (1)
b - a (1)
c - b (1)

See the cycle: a - b, b - c, c - a

After a swap of a,c you get:

a - a (1)
b - c (1)
c - b (1)   

Which is after ignoring first one and merge 2 & 3: 

c - b (2)

Which is even, you get to the result in one swap

Return 2

3. For instance: "a     b    c   a   b   c"
a - c (2)

One swap and you are good

因此,基本上在生成图形之后,将结果添加到每个边缘的权重/ 2(整数除法,例如7/3 = 3)

加上找到循环并添加到每个循环的结果长度1中

答案 3 :(得分:0)

version: '3.3'

services:
  
  app1:
    build: ./app1         
    image: "app1:latest"   
    container_name: app1
    ports:
      - "5001:5000"
    networks:
      - net1
  
  app2:
    build: ./app2
    image: "app2:latest"
    container_name: app2
    ports:
      - "5002:5000"
    networks:
      - net1

networks:
  net1:
    external: true