简体   繁体   中英

Possible Palindrome check from given string - efficient code needed

I am solving Palindrome index problem. The problem's difficulty level is easy and still I didn't pass all testcases :(. At most of the testcases, I got timeout and surprisingly one testcase failed as well. I googled about palindrome checking - there seems to be just two ways-

1. Using StringBuilder reverse() method.
2. Comparing characters till mid of the string length.

I tried both approaches and still no success.

Following is my attempt:

import java.util.Scanner;

public class PalindromeIndex {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int totalTestCases = Integer.parseInt(scanner.nextLine());

        String testString = null;
        StringBuffer outputString = new StringBuffer();
        for (int i = 0; i < totalTestCases; i++) {
            testString = scanner.nextLine();
            boolean is0thElementTrue = false;
            // One lettered word is always a palindrome so checked that
            // condition
            if (isPalindrome(testString) || testString.length() == 1) {
                outputString.append("-1");
            } else {
                if (isPalindrome(testString.substring(1))) {
                    outputString.append("0 ");
                    is0thElementTrue = true;
                }
                // See here j - index starts from 1 as initial case is
                // already
                // handled in above if-condition
                // Also see the condition checks [j < testString.length() -
                // 1;]
                // because (testString.length() - 1) is checked separately
                // after
                // this for loop.;
                boolean isLoopSuccessAtleastOnce = false;
                for (int j = 1; j < testString.length() - 1; j++) {
                    if (isPalindrome(testString.substring(0, j)
                            + testString.substring(j + 1, testString.length()))) {
                        isLoopSuccessAtleastOnce = true;
                        if (is0thElementTrue) {
                            outputString.append(j);
                            is0thElementTrue = false;
                        } else
                            outputString.append(" " + j);
                    }
                }
                if (isPalindrome(testString.substring(0,
                        testString.length() - 1))) {
                    if (isLoopSuccessAtleastOnce)
                        outputString.append(" " + (testString.length() - 1));
                    else
                        outputString.append((testString.length() - 1));
                }
            }
            outputString.append("\n");
        }
        System.out.println(outputString.toString());
        scanner.close();
    }

    private static boolean isPalindrome(String str) {
        StringBuilder strBuffer = new StringBuilder(str);
        return str.equalsIgnoreCase(strBuffer.reverse().toString());
    }

    private static boolean isPalindrome_2ndWay(String str) {
        int n = str.length();
        for (int i = 0; i < (n / 2) + 1; ++i) {
            if (str.charAt(i) != str.charAt(n - i - 1)) {
                return false;
            }
        }

        return true;
    }
}

Your solution is quadratic, this is definitely not what that site is looking for, you can do much better.

Consider this. If the first and last characters are not equal to each other, then one of them is the answer. If they are equal, the same logic can be applied to second and one before last characters. Etc.

The problem is stated as

figure out the index of the character on whose removal will make the string a palindrome. There will always be a valid solution. In case string is already palindrome, then -1 is also a valid answer

So it can be approached as follows:

  • Find the first character that makes the string NOT a palindrome, in other words, the first character that is different from the character at its "mirror" position.
  • If there is no such character, the string is already a palindrome.
  • If there is such character, find out whether removing it makes the string a palindrome. If it does, you have found the solution. If it doesn't, then the character at the mirror position must be the answer (the problem states that there always be a valid solution).

Finding the first character that makes the string not a palindrome is O(n). This operation will be executed at most two times per string, so the global solution is still O(n).

Here is a possible implementation. It has passed all tests

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class PalindromeIndex {

    public static void main(String[] args) {

        List<String> inputStrs = readInput();

        for (String inputStr : inputStrs) {
            int index = findIndexToRemove(inputStr);
            System.out.println(index);
        }
    }

    private static List<String> readInput() {
        int inputSize;
        List<String> inputStrs = new ArrayList<>();
        try(Scanner scanner = new Scanner(System.in)){
            inputSize = Integer.parseInt(scanner.nextLine());
            for (int i = 0; i < inputSize; i++) {
                inputStrs.add(scanner.nextLine());
            }
        }
        return inputStrs;
    }

    private static int findIndexToRemove(String inputStr){
        int i = findIndexOfDifference(inputStr);
        int result;
        if(i == -1){
            // String is a palindrome
            result = -1;
        } else {
            // Either the char at i or the char at the opposite side (length-1-i)
            // is the char to remove. Let's remove one of them and see
            // if the result is a palindrome
            if(findIndexOfDifference(removeChar(inputStr, i)) == -1){
                result = i;
            } else {
                result = inputStr.length() - 1 - i;
            }
        }
        return result;
    }

    /**
     * Returns the position of the first character that is not equal to the
     * character at its simmetric position (i.e. the char at the same position
     * counting from the opposite side of the string).
     */
    private static int findIndexOfDifference(String inputStr){
        int i=0, j=inputStr.length()-1;
        while(i<j && inputStr.charAt(i) == inputStr.charAt(j)){
            i++;
            j--;
        }
        int index;
        if(i<j){
            index = i;
        } else {
            index = -1;
        }
        return index;
    }

    private static String removeChar(String str, int charIndex){
        String firstPart = str.substring(0, charIndex);
        String secondPart = str.substring(Math.min(str.length(), charIndex+1), str.length());
        return firstPart + secondPart;
    }

}

Try a more efficient solution: use StringBuilder.deleteCharAt(int index) but beware that you either have to put it back or create a new StringBuilder from the string to test. Then you can just iterate over the characters, remove them and test the resulting string using your existing methods.

Small note:

Note: If the character at the given index is a supplementary character, this method does not remove the entire character.

... but I don't think this is to test your Unicode knowledge at this point.

This would be my answer:

String text = "Anita lava la tina";
text = text.toLowerCase().replaceAll(" ", "");

StringBuilder strb = new StringBuilder(text);

String text2 = strb.reverse().toString();

System.out.println("Palindrome: " + text.equals(text2));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM