├── .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 |
--------------------------------------------------------------------------------