【问题标题】:Find minimum length of substring to rearrange for palindromic string [closed]查找子串的最小长度以重新排列回文字符串[关闭]
【发布时间】:2021-12-18 17:53:37
【问题描述】:

有一个字符串s。重新排列以使字符串成为回文的子字符串的最小长度是多少。

例子:

输入: abbaabbca

输出: 4

我可以从索引4到7(abbc)重新排列子串,得到abbacabba

重排后保证有回文。


有没有办法通过修改 Manacher 或其他一些文本算法来解决它?

谢谢。

【问题讨论】:

  • 请编辑以展示您的作品。目前您只提供了一个高级别的要求,没有展示任何工作或具体问题。
  • 很抱歉我没有提供任何工作。但是,我所做的一切都是蛮力解决方案,这并不好。我想知道一些算法可以帮助我。感谢详细的解释,非常感谢。
  • 好吧,下次遇到这样的问题,我建议你试试手动解决,玩一玩,看看有什么可能。你必须对这些问题需要什么样的算法有一种“感觉”。

标签: c++ palindrome


【解决方案1】:

我认为标准文本处理算法并非如此。它是如此简单,您不需要它们 - 字符串只有一个重新洗牌的部分,因此可能会出现四种情况。

  1. 'ppssXXXXXXXpp'
  2. 'ppXXXXXsssspp'
  3. 'ppsssiiiXXXpp'
  4. 'ppXXXiiissspp'

在哪里

  • pp 是已经是回文的外部部分(可能为零)
  • XX是我们改组的部分
  • ss 是我们保留原样的部分(并重新洗牌 XX 以匹配它)
  • ii 是围绕中心的内部部分,也已经是回文(可能为零)

我们可以先检查和剪辑外部回文部分,留下'ssXXXXXXX''XXXXXssss''sssiiiXXX''XXXiiisss'

然后我们使用对称性——如果中间部分存在,我们可以任意选择我们保留哪一边,哪一边洗牌以适应另一边,所以我们只做一个。 当没有中间回文部分时,我们只需运行相同的检查,但从相反的方向开始,然后我们选择给出较短子串的那个

那么,让我们从头开始吧。我们将一个接一个地接一个字符 's--------' 'ss-------' 'sss------'

当字符串的其余部分不再与其余部分匹配时停止。

什么时候发生?当字符串的 'ssss... 部分已经吞噬了超过一半的字符出现时,那么它将在另一侧丢失,并且无法通过改组进行匹配。

另一方面,在通过字符串的中间之后,我们总是会吃掉超过一半的每个字符的出现次数。所以可能会出现三种情况。

  1. 我们没达到中间。在这种情况下,我们找到了要重新洗牌的字符串。 'sssXXXXXXXXXXXX'
  2. 我们到达中间。然后我们也可以搜索回文的内部部分,产生类似'ssssiiiiXXXX'
  3. 有一种特殊情况,您到达奇数字符串的中间 - 那里必须有一个奇数字符。如果不存在,则必须按照 1) 进行操作

生成的算法(在 java 中,already tried it here):

package palindrometest;

import java.io.*;
import java.util.*;
import java.util.stream.*;

class PalindromeTest {

    static int[] findReshuffleRange( String s ) {
        // first the easy part,
        //split away the already palindromatic start and end if there is any
        int lo = 0, hi = s.length()-1;
        while(true) {
            if( lo >= hi ) {
                return new int[]{0,0}; // entire string a palindrome
            }
            if( s.charAt(lo) != s.charAt(hi) ) {
                break;
            }
            lo++;
            hi--;
        }

        // now we compute the char counts and things based on them
        Map<Character,Integer> charCounts = countChars( s, lo, hi );
        
        if( !palindromePossible( charCounts ) ) {
            return null;
        }
        
        Map<Character,Integer> halfCounts = halfValues( charCounts );
        
        char middleChar = 0;
        if( (s.length() % 2) != 0 ) { // only an odd-sized string has a middle char
            middleChar = findMiddleChar( charCounts );
        }

        // try from the beginning first
        int fromStart[] = new int[2];
        if(  findMiddlePart( fromStart, s, lo, hi, halfCounts, middleChar, false ) ) {

            // if the middle palindromatic part exist, the situation is symmetric
            // we don't have to check the opposite direction
            return fromStart;
        }

        // try from the end
        int fromEnd[] = new int[2];
        findMiddlePart( fromEnd, s, lo, hi, halfCounts, middleChar, true );

        // take the shorter
        if( fromEnd[1]-fromEnd[0] < fromStart[1]-fromStart[0] ) {
            return fromEnd;
        } else {
            return fromStart;
        }
    }

    static boolean findMiddlePart( int[] result, String s, int lo, int hi, Map<Character,Integer> halfCounts, char middleChar, boolean backwards ) {
        Map<Character,Integer> limits = new HashMap<>(halfCounts);
        int pos, direction, end, oth;
        if( backwards ) {
            pos = hi;
            direction = -1;
            end = (lo+hi)/2; // mid rounded down
            oth = (lo+hi+1)/2; // mid rounded up
        } else {
            pos = lo;
            direction = 1;
            end = (lo+hi+1)/2; // mid rounded up
            oth = (lo+hi)/2; // mid rounded down
        }
        
        // scan until we run out of the limits
        while(true) {
            char c = s.charAt(pos);
            int limit = limits.get(c);
            if( limit <= 0 ) {
                break;
            }
            limits.put(c,limit-1);
            pos += direction;
        }
        
        // whether we reached the middle
        boolean middleExists = pos == end && ( oth != end || s.charAt(end) == middleChar );
        
        if( middleExists ) {
            // scan through the middle until we find the first non-palindromic character
            while( s.charAt(pos) == s.charAt(oth) ) {
                pos += direction;
                oth -= direction;
            }
        }
        
        // prepare the resulting interval
        if( backwards ) {
            result[0] = lo;
            result[1] = pos+1;
        } else {
            result[0] = pos;
            result[1] = hi+1;
        }
        return middleExists;
    }

    static Map<Character,Integer> countChars( String s, int lo, int hi ) {
        Map<Character,Integer> charCounts = new HashMap<>();
        for( int i = lo ; i <= hi ; i++ ) {
            char c = s.charAt(i);
            int cnt = charCounts.getOrDefault(c,0);
            charCounts.put(c,cnt+1);
        }
        return charCounts;
    }

    static boolean palindromePossible(Map<Character,Integer> charCounts) {
        int oddCnt = 0;
         for( int cnt : charCounts.values() ) {
            if( (cnt % 2) != 0 ) {
                oddCnt++;
                if( oddCnt > 1 ) {
                    return false; // can not be made palindromic
                }
            }
        }
        return true;
    }

    static char findMiddleChar( Map<Character,Integer> charCounts ) {
        Map<Character,Integer> halfCounts = new HashMap<>();
        for( Map.Entry<Character,Integer> e : charCounts.entrySet() ) {
            char c = e.getKey();
            int cnt = e.getValue();
            if( (cnt % 2) != 0 ) {
                return c;
            }
        }
        return 0;
    }
        
    static Map<Character,Integer> halfValues( Map<Character,Integer> charCounts ) {
        Map<Character,Integer> halfCounts = new HashMap<>();
        for( Map.Entry<Character,Integer> e : charCounts.entrySet() ) {
            char c = e.getKey();
            int cnt = e.getValue();
            halfCounts.put(c,cnt/2); // we round *down*
        }
        return halfCounts;
    }
    
    static String repeat(char c, int cnt ) {
        return cnt <= 0 ? "" : String.format("%"+cnt+"s","").replace(" ",""+c);
    }
    
    static void testReshuffle(String s ) {
        int rng[] = findReshuffleRange( s );
        if( rng == null ) {
            System.out.println("Result : '"+s+"' is not palindromizable");
        } else if( rng[0] == rng[1] ) {
            System.out.println("Result : whole '"+s+"' is a palindrome");
        } else {
            System.out.println("Result : '"+s+"'");
            System.out.println("          "+repeat('-',rng[0])+repeat('X',rng[1]-rng[0])+repeat('-',s.length()-rng[1]) );
        }
    }

    public static void main (String[] args) {
        testReshuffle( "abcdefedcba" );
        testReshuffle( "abcdcdeeba" );
        testReshuffle( "abcfdeedcba" );
        testReshuffle( "abcdeedbca" );
        testReshuffle( "abcdefcdeba" );
        testReshuffle( "abcdefgfcdeba" );
        testReshuffle( "accdefcdeba" );
    }
}

【讨论】:

  • 你已经发布了一个非常详细的答案,没有显示任何努力:没有代码,没有具体问题请不要这样做。
  • 我认为对新人苛刻并不是正确的做法。
  • 我并不想变得苛刻。问题是,许多人在课堂/家庭作业中发布各种挑战,但没有展示工作,然后求助于社区来解决他们的特定问题。在这种情况下,OP 根本没有展示他们的工作,所以......实际上,你为他们做了他们的工作。最好发表评论并要求 OP 编辑​​他们的问题以展示他们的工作、具体问题等。这不鼓励这种行为。
  • 假设他/她只是懒惰,而不是甚至不知道从哪里开始,苛刻。
【解决方案2】:

你可以这样使用

bool morethanone(string s, char c)
{
    // Count variable
    int res = 0;
 
    for (int i=0;i < s.length(); i++)
 
        // checking character in string
        if (s[i] == c)
            res++;
 
    if(res > 1) 
        return true;
    else
        return false;     
}

int getsubstringlength(string text)
{
    int result = 0;
    for (int i = 0; i < text.length(); i++)
    {
        if(morethanone(text, text[i]))
            result++;
    }
    return result / 2;
}

【讨论】:

  • 这不起作用。例如,在字符串 'aaaaabaaa' 上它说 8 它应该返回 2。
  • 你已经发布了一个非常详细的答案,没有显示任何努力:没有代码,没有具体问题请不要这样做。
猜你喜欢
  • 2016-08-01
  • 2015-01-02
  • 1970-01-01
  • 2018-07-19
  • 2016-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-19
相关资源
最近更新 更多