Concatenating two substrings to provide the largest possible palindromic string The code extracts two substrings from different strings and concatinating them to provide the largest possible palindromic string.

Objective:


  You have two strings, (a) and (b). Find a string, (c), such that:
  (c)=(d)+(e).
  
  (d),(e) can be expressed as  where (d) is a non-empty substring of (a)
  and (e) is a non-empty substring of (b). (c) is a palindromic string.
  The length of is as long as possible. For each of the pairs of strings
  (a) and (b) received as input, find and print string  on a new line.
  If you're able to form more than one valid string , print whichever
  one comes first alphabetically. If there is no valid answer, print -1
  instead.


How can I improve the performance of this code, reducing the compile time and keeping the functionality the same?

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

public class Solution {
    boolean isPalindrome(String s) {
  int n = s.length();
  for (int i=0;i&lt;(n / 2);++i) {
     if (s.charAt(i) != s.charAt(n - i - 1)) {
         return false;
     }
  }

  return true;
}

    public static void main(String[] args) {

        String result="";
         Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        for(int a=0; a&lt;n; a++)
            {int length1, length2, i,c,d,j;
        int max_length=0;
        String string1 = in.next();
        String sub1,sub2;
        String string2 = in.next();
        length2=string2.length();
        length1 = string1.length();   

      for( c = 0 ; c &lt;length1 ; c++ )
      {
         for( i = length1-c ; i &gt;0 ; i-- )
         {
            sub1 = string1.substring(c, c+i);
            for( d = 0 ; d &lt; length2 ; d++ )
      {
         for( j = length2-d ; j &gt;0 ; j-- )
         {
            sub2 = string2.substring(d, d+j);
            String temp=sub1+sub2;
              Solution obj= new Solution();
             if(temp.length()&gt;=max_length &amp;&amp; obj.isPalindrome(temp)==true)

                 {
                 if (max_length==temp.length())
                  {   if(temp.compareTo(result)&lt;0)
                     {
                     result=temp;
                  }}
                 else {
                     max_length=temp.length();
                 result=temp;
                  }
             }
         }
      }
         }
      }
             if(max_length==0)
                 System.out.println(-1);
             else
                 {
       System.out.println(result);
             result="";
             }
        }    /* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
    }
}
Minor tweak


    boolean isPalindrome(String s) {
  int n = s.length();
  for (int i=0;i&lt;(n / 2);++i) {
     if (s.charAt(i) != s.charAt(n - i - 1)) {
         return false;
     }
  }



Your indentation is all over the map.  Copy/paste error?  

More importantly, you can save some math by writing this 

    public static boolean isPalindrome(String s) {
        for (int i = 0, j = s.length() - 1; i &lt; j; ++i, --j) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }

        return true;
    }


Perhaps a good compiler would optimize them out, but your original was doing four math operations per iteration where this only does two (and an extra at declaration).  Not counting the comparisons for either.  

Small tweak

I'd rewrite main as 

        try (Scanner in = new Scanner(System.in))
        {
            for (int n = in.nextInt(); n &gt; 0; n--)
            {
                String string1 = in.next();
                String string2 = in.next();

                String palindrome = findLongestPalindrome(string1, string2);
                if (palindrome.isEmpty())
                {
                    System.out.println(-1);
                }
                else
                {
                    System.out.println(palindrome);
                }
            }
        }


Putting the algorithm in a separate method makes it easier to test and cleans up main by a lot.  

We don't need a separate loop variable for the test cases here.  If we decrement instead of increment, we can simplify that logic.  

Putting the Scanner in a try-with-resources eliminates a compiler warning about never closing the Scanner.  

Major tweaks


      for( c = 0 ; c &lt;length1 ; c++ )
      {
         for( i = length1-c ; i &gt;0 ; i-- )
         {
            sub1 = string1.substring(c, c+i);
            for( d = 0 ; d &lt; length2 ; d++ )
      {
         for( j = length2-d ; j &gt;0 ; j-- )
         {
            sub2 = string2.substring(d, d+j);
            String temp=sub1+sub2;
              Solution obj= new Solution();
             if(temp.length()&gt;=max_length &amp;&amp; obj.isPalindrome(temp)==true)

                 {
                 if (max_length==temp.length())
                  {   if(temp.compareTo(result)&lt;0)
                     {
                     result=temp;
                  }}
                 else {
                     max_length=temp.length();
                 result=temp;
                  }
             }
         }
      }
         }
      }



I changed to 

    public static String findLongestPalindrome(String a, String b) {
        String result = "";
        for (int aStart = 0 ; aStart &lt; a.length() ; aStart++ )
        {
            char firstLetter = a.charAt(aStart);
            for (int bEnd = b.lastIndexOf(firstLetter); bEnd &gt;= 0 ; bEnd = b.lastIndexOf(firstLetter, bEnd - 1) )
            {
                for (int aEnd = a.length() ; aEnd &gt; aStart ; aEnd-- )
                {
                    String aSubstr = a.substring(aStart, aEnd);
                    int requiredLength = Math.max(result.length() - aSubstr.length(), 0);
                    for (int bStart = 0 ; bStart &lt;= bEnd - requiredLength; bStart++ )
                    {
                        String candidate = aSubstr + b.substring(bStart, bEnd + 1);

                        if (candidate.length() &gt;= result.length() &amp;&amp; isPalindrome(candidate))
                        {
                            if (candidate.length() == result.length() &amp;&amp; candidate.compareTo(result) &gt;= 0)
                            {
                                continue;
                            }

                            result = candidate;
                        }
                    }
                }
            }
        }

        return result;
    }


Changed arbitrary parameter names to match the problem statement.  

Changed loop index variables to match their meaning relative to the parameters.  

Using lastIndexOf to find the endpoint of the second string means that we can skip a lot of substrings that will never work.  The original code had to take the substrings, join them, and then check if the result is a palindrome on all substrings.  This reduces down to only possible substrings.  

Determining the requiredLength allows us to skip joining and checking substrings that are too short to work.  

I declared variables just in time to be initialized.  This makes it easier to see what's happening.  

An alternative algorithm

The obvious brute force solution is to check every possible start and end points for both strings.  You optimized this by starting with the longest possible substrings, and I added tweaks to optimize that more.  But I actually think that was the wrong way to go.  Note that you still have to iterate over every letter to check if it is a palindrome.  You can save checking some of the substrings if you integrate the palindrome check with taking the substrings.  Instead of four nested loops with a linear time check in the middle, we can use just three nested loops.  Iterate up to the correct solution rather than check down to it.