├── .gitignore ├── WebContent ├── META-INF │ └── MANIFEST.MF └── WEB-INF │ └── web.xml ├── src └── com │ └── divideandconquer │ ├── palindrome │ ├── AbstractFactory.java │ ├── Parser.java │ ├── Recursive.java │ └── Manacher.java │ └── restjersey │ └── PalindromeService.java ├── README.md ├── .project └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | target/ 3 | .settings/ 4 | -------------------------------------------------------------------------------- /WebContent/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /src/com/divideandconquer/palindrome/AbstractFactory.java: -------------------------------------------------------------------------------- 1 | package com.divideandconquer.palindrome; 2 | 3 | public class AbstractFactory { 4 | public static final String typeSlow = "slow"; 5 | 6 | public static Parser Factory(String type) { 7 | if (type != null && type.equals(typeSlow)) { 8 | return new Recursive(); 9 | } 10 | else { 11 | return new Manacher(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/com/divideandconquer/palindrome/Parser.java: -------------------------------------------------------------------------------- 1 | package com.divideandconquer.palindrome; 2 | 3 | /** 4 | * A palindrome.Parser can find the largest palindrome in a given string 5 | * @author divideandconquer 6 | */ 7 | public interface Parser { 8 | /** 9 | * This function will examine the input string and find the largest palindrome it contains 10 | * @param input - a string to parse for palindromes 11 | * @return String - the largest palindrome found 12 | */ 13 | public String findLargestPalindrome(String input); 14 | } 15 | -------------------------------------------------------------------------------- /WebContent/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | java-palindrome-example 4 | 5 | Jersey Web Application 6 | com.sun.jersey.spi.container.servlet.ServletContainer 7 | 1 8 | 9 | 10 | Jersey Web Application 11 | /* 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java Palindrome Example 2 | This is a simple example of a Java web service powered by JAX-RS. 3 | This service uses the [Manacher algorithm](https://en.wikipedia.org/wiki/Longest_palindromic_substring) 4 | to find the longest palindrome in a given string. 5 | 6 | 7 | ## API 8 | 9 | `GET /java-palindrome-example/palindrome/` 10 | 11 | Parses the string provided and finds the largest palindrome that it contains. An optional query parameter 12 | of `type` can also be set to `slow` in this case the service will use a significantly slower recursive 13 | algorithm. 14 | 15 | ### Example 16 | 17 | `GET /java-palindrome-example/palindrome/testingpalindromefoobarraboof?type=slow` 18 | 19 | ```json 20 | { 21 | "palindrome": "foobarraboof" 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /src/com/divideandconquer/palindrome/Recursive.java: -------------------------------------------------------------------------------- 1 | package com.divideandconquer.palindrome; 2 | 3 | public class Recursive implements Parser{ 4 | 5 | /** 6 | * This function will examine the input string and find the largest palindrome it contains recursively 7 | * @param input - a string to parse for palindromes 8 | * @return String - the largest palindrome found 9 | */ 10 | public String findLargestPalindrome(String input) { 11 | String result = ""; 12 | if (input != null && input.length() > 0 ) { 13 | result = recursiveFindLargestPalindrome(input.toLowerCase()); 14 | } 15 | return result; 16 | } 17 | 18 | private String recursiveFindLargestPalindrome(String input) { 19 | String result = ""; 20 | 21 | if (input.equals(new StringBuilder(input).reverse().toString())) { 22 | result = input; 23 | } 24 | else { 25 | String a = recursiveFindLargestPalindrome(input.substring(1)); 26 | String b = recursiveFindLargestPalindrome(input.substring(0, input.length()-1)); 27 | if (a.length() > b.length()) { 28 | result = a; 29 | } 30 | else { 31 | result = b; 32 | } 33 | } 34 | 35 | return result; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | java-palindrome-example 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.wst.jsdt.core.javascriptValidator 10 | 11 | 12 | 13 | 14 | org.eclipse.jdt.core.javabuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.wst.common.project.facet.core.builder 20 | 21 | 22 | 23 | 24 | org.eclipse.wst.validation.validationbuilder 25 | 26 | 27 | 28 | 29 | org.eclipse.m2e.core.maven2Builder 30 | 31 | 32 | 33 | 34 | 35 | org.eclipse.m2e.core.maven2Nature 36 | org.eclipse.jem.workbench.JavaEMFNature 37 | org.eclipse.wst.common.modulecore.ModuleCoreNature 38 | org.eclipse.wst.common.project.facet.core.nature 39 | org.eclipse.jdt.core.javanature 40 | org.eclipse.wst.jsdt.core.jsNature 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/com/divideandconquer/restjersey/PalindromeService.java: -------------------------------------------------------------------------------- 1 | package com.divideandconquer.restjersey; 2 | 3 | /** 4 | * This service will accept strings and return the largest palindrome in that string 5 | * @author divideandconquer 6 | */ 7 | 8 | import javax.ws.rs.GET; 9 | import javax.ws.rs.Path; 10 | import javax.ws.rs.PathParam; 11 | import javax.ws.rs.QueryParam; 12 | import javax.ws.rs.Produces; 13 | import com.divideandconquer.palindrome.*; 14 | import org.json.JSONObject; 15 | 16 | @Path("/palindrome") 17 | public class PalindromeService { 18 | @Path("{input}") 19 | @GET 20 | @Produces("application/json") 21 | public String findLargestPalindrome(@PathParam("input") String input, @QueryParam("type") String type) { 22 | 23 | // get the requested parser type from the factory 24 | Parser p = AbstractFactory.Factory(type); 25 | 26 | // find the largest palindrome 27 | String ret = p.findLargestPalindrome(input); 28 | 29 | //encode to json 30 | JSONObject obj = new JSONObject(); 31 | obj.put("palindrome", ret); 32 | return obj.toString(); 33 | } 34 | 35 | // This exposes the service through the CLI 36 | public static void main(String[] args) { 37 | String type = ""; 38 | String input = ""; 39 | if (args.length >= 1) { 40 | input = args[0]; 41 | } 42 | if (args.length >= 2) { 43 | type = args[1]; 44 | } 45 | PalindromeService p = new PalindromeService(); 46 | System.out.println(p.findLargestPalindrome(input, type)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | java-palindrome-example 4 | java-palindrome-example 5 | 0.0.1-SNAPSHOT 6 | war 7 | 8 | src 9 | 10 | 11 | maven-compiler-plugin 12 | 3.3 13 | 14 | 1.8 15 | 1.8 16 | 17 | 18 | 19 | maven-war-plugin 20 | 2.6 21 | 22 | WebContent 23 | false 24 | 25 | 26 | 27 | 28 | 29 | 30 | asm 31 | asm 32 | 3.3.1 33 | 34 | 35 | com.sun.jersey 36 | jersey-bundle 37 | 1.19 38 | 39 | 40 | org.json 41 | json 42 | 20140107 43 | 44 | 45 | com.sun.jersey 46 | jersey-server 47 | 1.19 48 | 49 | 50 | com.sun.jersey 51 | jersey-core 52 | 1.19 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/com/divideandconquer/palindrome/Manacher.java: -------------------------------------------------------------------------------- 1 | package com.divideandconquer.palindrome; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * This class implements the Manacher algorithm for the Parser interface 7 | * @author divideandconquer 8 | */ 9 | public class Manacher implements Parser { 10 | 11 | final private static char BOUNDARY = '|'; 12 | 13 | /** 14 | * This function will examine the input string and find the largest palindrome it contains 15 | * using the Manacher algorithm 16 | * @param input - a string to parse for palindromes 17 | * @return String - the largest palindrome found 18 | */ 19 | public String findLargestPalindrome(String input) { 20 | String result = ""; 21 | if ( input != null && input.length() > 0 ) { 22 | char[] withBoundaries = addBoundaries(input.toLowerCase().toCharArray()); 23 | int[] p = new int[withBoundaries.length]; // holds the distance to the center of the palindrome including boundaries 24 | int c = 0; // c marks the center of the palindrome 25 | int r = 0; // r marks the right most boundary of the palindrome 26 | 27 | for ( int i = 1; i < withBoundaries.length-1; i++ ) { 28 | // if we still have a palindrome of more than 1 29 | // p[i] is the min of the difference between the center and the right 30 | // and the length of its mirror index's palindrome 31 | if (r > i) { 32 | int mirror = c*2-i; // the mirrored position of i around c 33 | p[i] = Math.min(r-i, p[mirror]); 34 | } 35 | 36 | //search for palindromes from this point 37 | while ((i + (1+p[i])) < withBoundaries.length && (i - (1+p[i])) >= 0 && withBoundaries[i + (1+p[i])] == withBoundaries[i - (1+p[i])]) { 38 | p[i]++; // we've found a bigger palindrome so ++ 39 | } 40 | 41 | // if the palindrome expands past r we must recenter it 42 | if ( (i+p[i]) > r ) { 43 | c = i; 44 | r = i+p[i]; 45 | } 46 | } 47 | 48 | // loop and find the longest 49 | int len = 0; c = 0; 50 | for (int i = 1; i < withBoundaries.length; i++ ) { 51 | if (len < p[i]) { 52 | len = p[i]; 53 | c = i; 54 | } 55 | } 56 | // substring the result 57 | char[] charResult = Arrays.copyOfRange(withBoundaries, c-len, c+len+1); 58 | // remove the boundaries 59 | result = String.valueOf(removeBoundaries(charResult)); 60 | } 61 | return result; 62 | } 63 | 64 | private static char[] addBoundaries(char[] orig) { 65 | //The derived string will comprise N*2 + 1 elements 66 | // with each letter of the original surrounded by boundary characters 67 | char[] result = new char[(orig.length*2) + 1]; 68 | for (int i = 0; i < result.length-1; i = i+2) { 69 | // for each letter in the orig string place a boundary before it 70 | result[i] = BOUNDARY; 71 | result[i+1] = orig[i/2]; 72 | } 73 | 74 | //add the last boundary to the end 75 | result[result.length-1] = BOUNDARY; 76 | return result; 77 | } 78 | 79 | private static char[] removeBoundaries(char[] derived) { 80 | // the derived string is N*2+1 long so we must substract 1 and divide by 2 81 | // in order to get back to the original length 82 | char[] result = new char[(derived.length-1)/2]; 83 | 84 | // just in case our boundary is in the string we can't use string replace / regex 85 | // to help us here. 86 | // Instead we loop and rebuild the string 87 | for ( int i = 0; i < result.length; i++) { 88 | result[i] = derived[i*2+1]; 89 | } 90 | return result; 91 | } 92 | } 93 | --------------------------------------------------------------------------------