簡體   English   中英

找到 substring 的最小長度以重新排列回文字符串

[英]Find minimum length of substring to rearrange for palindromic string

有一個字符串 s。 substring 的最小長度是多少,以重新排列以制作字符串的回文。

例子:

輸入: abbaabbca

Output: 4

我可以重新排列 substring 從索引4 到 7 (abbc) ,並得到abbacabba

保證重排后有回文。


有沒有辦法通過修改 Manacher 或其他一些文本算法來解決它?

謝謝。

我認為標准文本處理算法並非如此。 它是如此簡單,您不需要它們 - 字符串只有一個重新洗牌的部分,因此可能會出現四種情況。

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

在哪里

  • pp是已經是回文的外部部分(可能為零)
  • XX是我們重新洗牌的部分
  • ss是我們保留原樣的部分(並重新調整 XX 以匹配它)
  • ii是圍繞中心的內部部分,也已經是回文(可能為零)

我們可以先檢查和剪輯外部回文部分,留下'ssXXXXXXX''XXXXXssss''sssiiiXXX''XXXiiisss'

然后我們使用對稱性——如果中間部分存在,我們可以任意選擇我們保留哪一邊,哪一邊洗牌以適應另一邊,所以我們只做一個。 當沒有中間回文部分時,我們簡單地運行相同的檢查,但從相反的方向開始,然后我們選擇給出較短 substring 的那個

所以,讓我們從頭開始。 我們將簡單地在另一個字符之后取一個字符's--------' 'ss-------' 'sss------'

並在字符串的 rest 不再與 rest 匹配時停止。

什么時候發生? 當字符串的 'ssss... 部分已經吞噬了超過一半的字符出現時,那么它將在另一側丟失並且無法通過改組來匹配。

另一方面,在通過字符串的中間之后,我們總是會吃掉超過一半的每個字符的出現次數。 所以會出現三種情況。

  1. 我們沒有達到中間。 在這種情況下,我們找到了要重新洗牌的字符串。 'sssXXXXXXXXXXXX'
  2. 我們到達中間。 然后我們也可以搜索回文的內部部分,產生類似'ssssiiiiXXXX'
  3. 有一種特殊情況,您到達奇數字符串的中間 - 那里必須有一個奇數字符。 如果它不存在,您將不得不按照 1) 進行操作

生成的算法(在 java 中,已經在這里嘗試過):

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" );
    }
}

你可以這樣使用

bool morethanone(string s, char c)
{
    // Count variable
    bool res = false;
 
    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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM