309 lines
10 KiB
Java
309 lines
10 KiB
Java
package com.jwetherell.algorithms.strings;
|
|
|
|
import java.util.BitSet;
|
|
import java.util.StringTokenizer;
|
|
|
|
/**
|
|
* This class contains methods for modifying text.
|
|
*
|
|
* @author Justin Wetherell <phishman3579@gmail.com>
|
|
*/
|
|
public class StringFunctions {
|
|
|
|
private static final char SPACE = ' ';
|
|
|
|
public static final String reverseWithStringConcat(String string) {
|
|
String output = new String();
|
|
for (int i = (string.length() - 1); i >= 0; i--) {
|
|
output += (string.charAt(i));
|
|
}
|
|
return output;
|
|
}
|
|
|
|
public static final String reverseWithStringBuilder(String string) {
|
|
final StringBuilder builder = new StringBuilder();
|
|
for (int i = (string.length() - 1); i >= 0; i--) {
|
|
builder.append(string.charAt(i));
|
|
}
|
|
return builder.toString();
|
|
}
|
|
|
|
public static final String reverseWithStringBuilderBuiltinMethod(String string) {
|
|
final StringBuilder builder = new StringBuilder(string);
|
|
return builder.reverse().toString();
|
|
}
|
|
|
|
public static final String reverseWithSwaps(String string) {
|
|
final char[] array = string.toCharArray();
|
|
final int length = array.length - 1;
|
|
final int half = (int) Math.floor(array.length / 2);
|
|
|
|
char c;
|
|
for (int i = length; i >= half; i--) {
|
|
c = array[length - i];
|
|
array[length - i] = array[i];
|
|
array[i] = c;
|
|
}
|
|
return String.valueOf(array);
|
|
}
|
|
|
|
public static final String reverseWithXOR(String string) {
|
|
final char[] array = string.toCharArray();
|
|
final int length = array.length;
|
|
final int half = (int) Math.floor(array.length / 2);
|
|
|
|
for (int i = 0; i < half; i++) {
|
|
array[i] ^= array[length - i - 1];
|
|
array[length - i - 1] ^= array[i];
|
|
array[i] ^= array[length - i - 1];
|
|
}
|
|
return String.valueOf(array);
|
|
}
|
|
|
|
public static final String reverseWordsByCharWithAdditionalStorage(String string) {
|
|
final StringBuilder builder = new StringBuilder();
|
|
final int length = string.length() - 1;
|
|
final StringBuilder temp = new StringBuilder();
|
|
|
|
char c = 0;
|
|
int index = 0;
|
|
int last = string.length();
|
|
for (int i = length; i >= 0; i--) {
|
|
c = string.charAt(i);
|
|
if (c == SPACE || i == 0) {
|
|
index = (i == 0) ? 0 : i + 1;
|
|
temp.append(string.substring(index, last));
|
|
if (index != 0)
|
|
temp.append(c);
|
|
builder.append(temp);
|
|
temp.delete(0, temp.length());
|
|
last = i;
|
|
}
|
|
}
|
|
|
|
return builder.toString();
|
|
}
|
|
|
|
public static final String reverseWordsUsingStringTokenizerWithAdditionalStorage(String string) {
|
|
final StringTokenizer st = new StringTokenizer(string);
|
|
String output = new String();
|
|
while (st.hasMoreTokens()) {
|
|
output = (st.nextToken()) + ' ' + output;
|
|
}
|
|
|
|
return output.trim();
|
|
}
|
|
|
|
public static final String reverseWordsUsingSplitWithAdditionalStorage(String string) {
|
|
final StringBuilder builder = new StringBuilder();
|
|
final String[] temp = string.split(" ");
|
|
|
|
for (int i = (temp.length - 1); i >= 0; i--) {
|
|
builder.append(temp[i]).append(' ');
|
|
}
|
|
return builder.toString().trim();
|
|
}
|
|
|
|
public static final String reverseWordsInPlace(String string) {
|
|
char[] chars = string.toCharArray();
|
|
|
|
int lengthI = 0;
|
|
int lastI = 0;
|
|
int lengthJ = 0;
|
|
int lastJ = chars.length - 1;
|
|
|
|
int i = 0;
|
|
char iChar = 0;
|
|
char jChar = 0;
|
|
while (i < chars.length && i <= lastJ) {
|
|
iChar = chars[i];
|
|
if (iChar == SPACE) {
|
|
lengthI = i - lastI;
|
|
for (int j = lastJ; j >= i; j--) {
|
|
jChar = chars[j];
|
|
if (jChar == SPACE) {
|
|
lengthJ = lastJ - j;
|
|
swapWords(lastI, i - 1, j + 1, lastJ, chars);
|
|
lastJ = lastJ - lengthI - 1;
|
|
break;
|
|
}
|
|
}
|
|
lastI = lastI + lengthJ + 1;
|
|
i = lastI;
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return String.valueOf(chars);
|
|
}
|
|
|
|
private static final void swapWords(int startA, int endA, int startB, int endB, char[] array) {
|
|
int lengthA = endA - startA + 1;
|
|
int lengthB = endB - startB + 1;
|
|
|
|
int length = lengthA;
|
|
if (lengthA > lengthB)
|
|
length = lengthB;
|
|
|
|
int indexA = 0;
|
|
int indexB = 0;
|
|
char c = 0;
|
|
for (int i = 0; i < length; i++) {
|
|
indexA = startA + i;
|
|
indexB = startB + i;
|
|
|
|
c = array[indexB];
|
|
array[indexB] = array[indexA];
|
|
array[indexA] = c;
|
|
}
|
|
|
|
if (lengthB > lengthA) {
|
|
length = lengthB - lengthA;
|
|
int end = 0;
|
|
for (int i = 0; i < length; i++) {
|
|
end = endB - ((length - 1) - i);
|
|
c = array[end];
|
|
shiftRight(endA + i, end, array);
|
|
array[endA + 1 + i] = c;
|
|
}
|
|
} else if (lengthA > lengthB) {
|
|
length = lengthA - lengthB;
|
|
for (int i = 0; i < length; i++) {
|
|
c = array[endA];
|
|
shiftLeft(endA, endB, array);
|
|
array[endB + i] = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static final void shiftRight(int start, int end, char[] array) {
|
|
for (int i = end; i > start; i--) {
|
|
array[i] = array[i - 1];
|
|
}
|
|
}
|
|
|
|
private static final void shiftLeft(int start, int end, char[] array) {
|
|
for (int i = start; i < end; i++) {
|
|
array[i] = array[i + 1];
|
|
}
|
|
}
|
|
|
|
public static final boolean isPalindromeWithAdditionalStorage(String string) {
|
|
final String reversed = new StringBuilder(string).reverse().toString();
|
|
return string.equals(reversed);
|
|
}
|
|
|
|
public static final boolean isPalindromeInPlace(String string) {
|
|
final char[] array = string.toCharArray();
|
|
final int length = array.length - 1;
|
|
final int half = Math.round(array.length / 2);
|
|
char a, b;
|
|
for (int i = length; i >= half; i--) {
|
|
a = array[length - i];
|
|
b = array[i];
|
|
if (a != b)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static final String[] generateSubsets(String inputString) {
|
|
final int length = inputString.length();
|
|
final int size = (int) Math.pow(2, length);
|
|
final BitSet[] sets = new BitSet[size];
|
|
final String[] output = new String[size];
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
final BitSet set = new BitSet(size);
|
|
final StringBuilder builder = new StringBuilder();
|
|
if (i > 0) {
|
|
for (int j = length - 1; j >= 0; j--) {
|
|
if (j == length - 1) {
|
|
if (i % 2 != 0)
|
|
set.set(j, true);
|
|
} else {
|
|
boolean prev = sets[i - 1].get(j);
|
|
boolean next = true;
|
|
for (int k = j + 1; k < length; k++) {
|
|
next = next && sets[i - 1].get(k);
|
|
}
|
|
if (next)
|
|
prev = !prev;
|
|
set.set(j, prev);
|
|
}
|
|
if (set.get(j))
|
|
builder.append(inputString.charAt(j));
|
|
}
|
|
}
|
|
sets[i] = set;
|
|
output[i] = builder.toString();
|
|
}
|
|
return output;
|
|
}
|
|
|
|
/** recursive **/
|
|
public static final int levenshteinDistanceRecursive(String s, String t) {
|
|
final int sLength = s.length();
|
|
final int tLength = t.length();
|
|
final char[] sChars = s.toCharArray();
|
|
final char[] tChars = t.toCharArray();
|
|
|
|
int cost = 0;
|
|
if ((sLength > 0 && tLength > 0) && sChars[0] != tChars[0])
|
|
cost = 1;
|
|
|
|
if (sLength == 0)
|
|
return tLength;
|
|
else if (tLength == 0)
|
|
return sLength;
|
|
else {
|
|
final int min1 = levenshteinDistanceRecursive(s.substring(1), t) + 1;
|
|
final int min2 = levenshteinDistanceRecursive(s, t.substring(1)) + 1;
|
|
final int min3 = levenshteinDistanceRecursive(s.substring(1), t.substring(1)) + cost;
|
|
|
|
int minOfFirstSet = Math.min(min1, min2);
|
|
return (Math.min(minOfFirstSet, min3));
|
|
}
|
|
}
|
|
|
|
/** iterative - dynamic programming **/
|
|
public static final int levenshteinDistanceIterative(String string1, String string2) {
|
|
final char[] s = string1.toCharArray();
|
|
final char[] t = string2.toCharArray();
|
|
final int m = s.length;
|
|
final int n = t.length;
|
|
|
|
// for all i and j, d[i,j] will hold the Levenshtein distance between
|
|
// the first i characters of s and the first j characters of t
|
|
// note that d has (m+1)*(n+1) values
|
|
final int[][] d = new int[m+1][n+1];
|
|
|
|
// source prefixes can be transformed into empty string by
|
|
// dropping all characters
|
|
for (int i=1; i<=m; i++)
|
|
d[i][0] = i;
|
|
|
|
// target prefixes can be reached from empty source prefix
|
|
// by inserting every character
|
|
for (int j=1; j<=n; j++)
|
|
d[0][j] = j;
|
|
|
|
int substitutionCost;
|
|
for (int j=1; j<=n; j++) {
|
|
for (int i=1; i<=m; i++) {
|
|
if (s[i-1] == t[j-1])
|
|
substitutionCost = 0;
|
|
else
|
|
substitutionCost = 1;
|
|
|
|
int minOfInsAndDel = Math.min(d[i-1][j] + 1, // deletion
|
|
d[i][j-1] + 1); // insertion
|
|
d[i][j] = Math.min(minOfInsAndDel, // minimum of insert and delete
|
|
d[i-1][j-1] + substitutionCost); // substitution
|
|
}
|
|
}
|
|
return d[m][n];
|
|
}
|
|
}
|