Elegant solution to replace substrings - java

I have a challenging problem that I am having trouble with. I have an unmodified string, for instance abcdefg and an array of objects that contains a string and indices.
For instance, object 1 contains d and indices [1, 2];
Then I would replace whatever letter is at substring [1,2] with d, with the resulting string looking like adcdefg.
The problem comes when the replacing text is of different length then the replaced text. I need some way to keep track of the length changes or else the indices of further replacements would be inaccurate.
Here is what I have so far:
for (CandidateResult cResult : candidateResultList) {
int[] index = cResult.getIndex();
finalResult = finalResult.substring(0, index[0]) + cResult.getCandidate()
+ finalResult.substring(index[1], finalResult.length()); //should switch to stringbuilder
}
return finalResult;
This does not take care of the corner case mentioned above.
Additionally, this is not homework if anyone was wondering. This is actually for an ocr trainer program that I'm creating.

Here's an implementation, I haven't tested it yet but you can try to get an idea. I'll add comments to the code as needed.
/** This class represents a replacement of characters in the original String, s[i0:if],
* with a new string, str.
**/
class Replacement{
int s, e;
String str;
public Replacement(int s, int e, String str){
this.s = s;
this.e = e;
this.str = str;
}
}
String stringReplace(String str, List<Replacement> replacements){
// Sort Replacements by starting index
Collections.sort(replacements, new Comparator<Replacement>(){
#Override public int compare(Replacement r1, Replacement r2){
return Integer.compare(r1.s, r2.s);
}
};
StringBuilder sb = new StringBuilder();
int repPos = 0;
for(int i = 0; i < str.length; i++){
Replacement rep = replacements.get(repPos);
if(rep.s == i){ // Replacement starts here, at i == s
sb.append(rep.str); // Append the replacement
i = rep.e - 1; // Advance i -> e - 1
repPos++; // Advance repPos by 1
} else {
sb.append(str.charAt(i)); // No replacement, append char
}
}
return sb.toString();
}
[Edit:] After seeing ptyx's answer, I think that way is probably more elegant. If you sort in reverse order, you shouldn't have to worry about the different lengths:
String stringReplace(String str, List<Replacement> replacements){
// Sort Replacements in reverse order by index
Collections.sort(replacements, new Comparator<Replacement>(){
#Override public int compare(Replacement r1, Replacement r2){
return -Integer.compare(r1.s, r2.s); // Note reverse order
}
};
// By replacing in reverse order, shouldn't affect next replacement.
StringBuilder sb = new StringBuilder(str);
for(Replacement rep : replacements){
sb.replace(rep.s, rep.e, rep.str);
}
return sb.toString();
}

Assuming no overlapping ranges to replace, process your replacements in reverse position order - done.
It doesn't matter what you replace [5-6] with, it will never modify [0-4] therefore you don't need to bother about any index mapping for, for example: [1,2]

This seems to do as you ask, basically you just translate the replacement based on previous inserts
public static void main(String[] args) {
Replacer[] replacers = {
new Replacer(new int[]{ 1 , 2 }, "ddd") ,
new Replacer(new int[]{ 2 , 3 }, "a")
};
System.out.println(
m("abcdefg", replacers));
}
public static String m(String s1, Replacer[] replacers){
StringBuilder builder = new StringBuilder(s1);
int translate = 0;
for (int i = 0 ; i < replacers.length ; i++) {
translate += replacers[i].replace(builder, translate);
}
return builder.toString();
}
public static class Replacer{
int[] arr;
String toRep;
public Replacer(int[] arr, String toRep) {
this.arr = arr;
this.toRep = toRep;
}
public int replace(StringBuilder b, int translate){
b.replace(arr[0] + translate, arr[1] + translate, toRep);
return arr[1];
}
}

Related

if the characters are same then consider it only once in java language

So, i am basically new to java ,and there was this question on our programming test
input:ww:ii:pp:rr:oo
if the alphabets are same then consider only once
output:wipro
so i was able to remove the : from the input and was also able to separate them
my current output :[w,w,i,i,p,p,r,r,o,o]
but i am unable to consider the same characters only once,its been nearly 35 min :_(
String txt="ww:ii:pp::rr:oo";
String[] result= txt.split(":");
System.out.println(Arrays.toString(result));//1
String n11="";
for(String str:result){
n11 += str;
}
System.out.println(n11);//2
result=n11.split("");
System.out.println(Arrays.toString(result));//3
String n12="";
int i=0;
for(String i:result){
if(i.equals(i+1)){
continue;
}
else {
n12=n12+i;
}
}
System.out.println(n12);//4
}
output
[ww, ii, pp, , rr, oo]
wwiipprroo
[w, w, i, i, p, p, r, r, o, o]
[nullw, nullw, nulli, nulli, nullp, nullp, nullr, nullr, nullo, nullo]
Example:
public class GFG
{
/* Method to remove duplicates in a sorted array */
static String removeDupsSorted(String str)
{
int res_ind = 1, ip_ind = 1;
// Character array for removal of duplicate characters
char arr[] = str.toCharArray();
/* In place removal of duplicate characters*/
while (ip_ind != arr.length)
{
if(arr[ip_ind] != arr[ip_ind-1])
{
arr[res_ind] = arr[ip_ind];
res_ind++;
}
ip_ind++;
}
str = new String(arr);
return str.substring(0,res_ind);
}
/* Method removes duplicate characters from the string
This function work in-place and fills null characters
in the extra space left */
static String removeDups(String str)
{
// Sort the character array
char temp[] = str.toCharArray();
//Arrays.sort(temp);
str = new String(temp);
// Remove duplicates from sorted
return removeDupsSorted(str);
}
// Driver Method
public static void main(String[] args)
{
String str = "ww:ii:pp:rr:oo";
String str1 = str.replaceAll(":","");
System.out.println(removeDups(str1));
}
}
The source is taken from www.geeksforgeeks.org And added String str1 = str.replaceAll(":","");
Output:
Your first step is right. But, you have an error in i.equals(i+1) since i + 1 isn't is the next element. You should iterate the array like this:
for (int i = 0; i < result.length - 1; i ++) {
if (result[i].equals(result[i + 1])) {
// do the remove operation.
}
}

How I can generate Concatenation between two chars based on previous combine using java 8?

I'm working on an a problem trying to generate all the possible combination between two chars based on early generated combine using java 8
for example :
private static final String LETTER_RANGE = "abcdefghijklmnopqrstuvwxz";
from this letter rang I want to extract all differents combine between two chars XX
for example :
zz,zx,zw....za
xz,xx,xw....xa
..,..,..,..,..
az,ax,aw... aa
My problem is I need to generate those combination at runtime based on previous combine :
String value = generate("zx") // this should return 'zw'
Can any one helpe me on any idea how can use java 8 loops,Stream,String to do this Thanks in advance
You can use simple character arithmetics. As chars can be incremented and compared:
final List<String> permutations = new ArrayList<>(26 * 26);
for (char[] array = {'a', 'a'}; array[0] <= 'z'; array[0]++) {
for (array[1] = 'a'; array[1] <= 'z'; array[1]++) {
permutations.add(new String(array));
}
}
This piece of code creates every combination of all characters between a and z inclusive and adds them to a List.
This is possible because in ASCII the character value of a (97) is smaller than the one from z (122).
I've also used some optimizations, like the use of an array inside the for-loop to hold the current combination of 2 chars. This array can then also be directly used to create a new string, with the string constructor: String(char[]).
Tools one might use are:
char ch = LETTER_RANGE.charAt(2); // 'c'
int ix = LETTER_RANGE.indexOf(ch); // 2
Using the single char[] array is probably much easier, so the method might look like:
String next(String combination) {
char[] chars = combination.toCharArray();
char ch = chars[1];
if (...) {
}
return new String(chars);
}
Sounds like very bad solution of task for me. But if you really need it, it can be done like this.
private static final String LETTER_RANGE = "abcdefghijklmnopqrstuvwxz";
public String findNext(String prev) {
if(prev==null || prev.length()<2)
throw new RuntimeException("Invalid argument");
int char1Index = LETTER_RANGE.indexOf(prev.charAt(0));
int char2Index = LETTER_RANGE.indexOf(prev.charAt(1));
char2Index--;
if (char2Index < 0) {
char1Index--;
char2Index = LETTER_RANGE.length() - 1;
}
if (char1Index < 0) {
return null;// or what you need here.
}
return new String(new char[]{LETTER_RANGE.charAt(char1Index), LETTER_RANGE.charAt(char2Index)});
}
And the task find all Concatenation between two chars from predefined list I would do like this
public List findAll() {
List<String> result=new ArrayList<>();
char[] chars=LETTER_RANGE.toCharArray();
for(int i=0;i<chars.length;i++)
for(int j=0;j<chars.length;j++)
result.add(new String(new char[]{chars[i],chars[j]}));
return result;
}
The pattern in your example reminds me of Excel columns. Excel names its columns with letters from A to Z, and then the sequence goes AA, AB, AC... AZ, BA, BB, etc. So if we interpret your combinations as Excel column titles the task could be reworded to:
Given a column title as appears in an Excel sheet, find the next
column title (or previous as shown in your expected output).
To do this you can write a method that accepts a string as a parameter (like "zf") and returns the actual column number. And then add or substract 1 to get the number of the next or previos column and convert the number back to string. Example:
public final class ExcelColumn {
public static void main(String[] args) {
String str = "zx";
System.out.println(getPreviousColumn(str));
}
public static int toColumnNumber(String column) {
int result = 0;
for (int i = 0; i < column.length(); i++) {
result *= 26;
result += column.charAt(i) - 'a' + 1;
}
return result;
}
public static String toColumnName(int number) {
final StringBuilder sb = new StringBuilder();
int num = number - 1;
while (num >= 0) {
int numChar = (num % 26) + 97;
sb.append((char)numChar);
num = (num / 26) - 1;
}
return sb.reverse().toString();
}
public static String getNextColumn(String s) {
return toColumnName( toColumnNumber(s)+1);
}
public static String getPreviousColumn(String s) {
return toColumnName( toColumnNumber(s)-1);
}
}
ToDo:
Input validiation and
exception handling
Pros:
You can use this even if your combined string length is > 2
can be easily modified to use with uppercase letters
You can do something like from 'be' to 'cf' to generate all combinations which fall in this range if necessary
Cons:
May be to much code for a simple task. Look at #Andrii Vdovychenko's
comment which solves the problem in few lines

Efficient Algorithm for character replacement in a string

I have two strings
111TTT0000TT11T00
001101
Now I want to replace all appearances of T in string 1 with character from string 2. Like first T with 0, second T with 0, third T with 1 and so on.
One way of doing so is using while loop and compare every character but in programming sense that's not a good way of acheiving it. Can anybody solve it with better algorithm using JAVA?
public void DataParse(String point, String code)
{
//////////tln("Point:"+point);
//////////tln("code:"+code);
// //////////tln(baseString_temp);
int counter=0;
while(baseString_temp.contains(point))
{
if(code!=null)
{
String input=String.valueOf(code.charAt(counter));
//zzzzz(input);
baseString_temp=baseString_temp.replaceFirst(point,input);
counter=counter+1;
}
}
////////////System.out(baseString_temp);
}
Every time, when you use contains and replaceFirst, you force your program enumerate string's character from begining. I believe it will be better to do it in single pass:
public static String replaceToken(String primary, String secondary, char token) {
char [] charArray =primary.toCharArray();
int counter = 0;
for(int i=0; i<charArray.length; i++){
if(charArray[i]==token){
charArray[i] = secondary.charAt(counter);
counter++;
if(counter>=secondary.length()) break;
}
}
return new String(charArray);
}
public static void main(String[] args) {
String result = replaceToken("111TTT0000TT11T00", "001101", 'T');
}
If you realy would like to use RegExp so much, then here you are:
public static String replaceSequence(String primary, String secondary, String sequence){
Pattern pattern = Pattern.compile(sequence + "+");
Matcher matcher = pattern.matcher(primary);
int counter = 0;
char [] charArray = primary.toCharArray();
while(matcher.find() && counter<secondary.length()){
for(int i = matcher.start(); i<matcher.end(); i++){
charArray[i] = secondary.charAt(counter++);
if(counter>=secondary.length()) break;
}
}
return new String(charArray);
}
But, based on description of your task, I prefer first approach.
There's a couple of things. Because Strings are immutable,
baseString_temp=baseString_temp.replaceFirst(point,input);
will always create a new String object (Also, it goes through the string from the beginning, looking for point). If you use a StringBuilder, you only allocate memory once, and then you can mutate it. Actually, using an array like in Ken's answer would be even better, as it allocates less and has less overhead from method calls.
Also, I'd imagine contains() uses a loop of its own, and in the worst case goes to the end of the string. You only need to iterate over the string once, and replace as you go along.
Working example:
public class Test {
private static String replace(char what, String input, String repls) {
StringBuilder sb = new StringBuilder(input);
int replIdx = 0;
for (int i = 0; i < input.length(); i++) {
if (input.charAt(i) == what) {
sb.setCharAt(i, repls.charAt(replIdx++));
}
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(replace('T', "111TTT0000TT11T00", "001101"));
}
}

replace string one with instance of second string remove and replaced with third string without using replaceAll

This is what I came up with, but the last test case doesn't work. Any suggestions?
public class tester
{
public static String replaceAll(String a, String b, String c){
for(;;){
int i = a.indexOf(b);
if(i==-1){
break;
}
a = a.substring(0,i)+ c + a.substring(i + b.length());
}
return a;
}
public static void main(String args[]){
System.out.println(replaceAll("hello my friend, how are you?", "h", "y"));
System.out.println(replaceAll("CS221 is great!!","great","awesome"));
System.out.println(replaceAll("aaaa","a","aaa"));
}
}
Assuming I'm reading your code right:
Seeks "a" in "aaaa". Finds it at index zero.
Adds "aaa" to the start of "aaaa" yielding "aaaaaaa".
Seeks "a" in "aaaaaaa". Finds it at index zero.
Adds "aaa" to "aaaaaaa" yielding "aaaaaaaaaa".
...
So the string will keep growing indefinitely. You could look into updating the search index by the length of the inserted text to make sure you don't explode your strings like this.
Also, if you're going to be doing a lot of string concatenation, I'd recommend using a StringBuilder to prevent loads of copying.
Which is to say that something like this should work:
public static String replaceAll(String a, String b, String c){
StringBuilder sb = new StringBuilder();
int i = 0;
while(i < a.length()){
int j = a.indexOf(b, i);
if(j == -1){
sb.append(a.substring(i, a.length()));
i = a.length();
} else {
sb.append(a.substring(i, j));
sb.append(c);
i = j + b.length();
}
}
return sb.toString();
}
EDIT - added solution.

Interleaving of two strings

I have two strings str1 and str2.
Is there any algorithm that can be used in order to print out all interleavings of the two strings using recursion?
Update:
public class Interleave {
private String resultString[] = new String[10];
private String[] interStr(String str1, String str2){
int n = ((Factorial.factorial(str1.length() + str2.length())) / (Factorial.factorial(str1.length()) * Factorial.factorial(str2.length())));
//n is number of interleavings based on (str1.length()+str2.length())! / (str1.length()! * str2.length()!)
if(str1.length() == 0){
resultString[0] = str2;
return resultString;
}
if(str2.length() == 0){
resultString[0] = str1;
return resultString;
}
else{
for(int i = 0; i < n; i++){
resultString[i]= str1.substring(0, 1) + interStr(str1.substring(1), str2.substring(1));
}
}
return resultString;
}
public static void main(String[] args) {
Interleave obj = new Interleave();
obj.interStr("12", "abc");
for(int i = 0; i < obj.resultString.length; i ++){
System.out.println(obj.resultString[i]);
}
}
}
The question simply asked whether a recursive algorithm exists for the problem, and the answer is yes. To find it, look for the base case and then for the "step".
The base case is when one of the two strings are empty:
interleave(s1, "") = {s1}
interleave("", s2) = {s2}
Notice the order of the arguments doesn't really matter, because
interleave("ab", "12") = {"ab12", "a1b2", "1ab2", "a12b", "1a2b", "12ab"} = interleave("12", "ab")
So since the order doesn't matter we'll look at recursing on the length of the first string.
Okay so let's see how one case leads to the next. I'll just use a concrete example, and you can generalize this to real code.
interleave("", "abc") = {"abc"}
interleave("1", "abc") = {"1abc", "a1bc", "ab1c", "abc1"}
interleave("12", "abc") = {"12abc", "1a2bc", "1ab2c", "1abc2", "a12bc", "a1b2c", "a1bc2", "ab12c", "ab1c2" "abc12"}
So everytime we added a character to the first string, we formed the new result set by adding the new character to all possible positions in the old result set. Let's look at exactly how we formed the third result above from the second. How did each element in the second result turn into elements in the third result when we added the "2"?
"1abc" => "12abc", "1a2bc", "1ab2c", "1abc2"
"a1bc" => "a12bc", "a1b2c", "a1bc2"
"ab1c" => "ab12c", "ab1c2"
"abc1" => "abc12"
Now look at things this way:
"1abc" => {1 w | w = interleave("2", "abc")}
"a1bc" => {a1 w | w = interleave("2", "bc")}
"ab1c" => {ab1 w | w = interleave("2", "c")}
"abc1" => {abc1 w | w = interleave("2", "")}
Although one or two examples doesn't prove a rule in general, in this case you should be able to infer what the rule is. You will have a loop, with recursive calls inside it.
This is actually a little more fun to do with pure functional programming, but you tagged the question with Java.
Hopefully this is a start for you. If you get stuck further you can do a web search for "interleaving strings" or "interleaving lists". There are some solutions out there.
EDIT:
Okay I just wrote the silly thing! It's a lot of fun to write these things in scripting languages, so I thought it would be great to see what it looked like in Java. Not as bad as I thought it would be! Here it is, packaged as an entire Java application.
import java.util.ArrayList;
import java.util.List;
public class Interleaver {
/**
* Returns a list containing all possible interleavings of two strings.
* The order of the characters within the strings is preserved.
*/
public static List<String> interleave(String s, String t) {
List<String> result = new ArrayList<String>();
if (t.isEmpty()) {
result.add(s);
} else if (s.isEmpty()) {
result.add(t);
} else {
for (int i = 0; i <= s.length(); i++) {
char c = t.charAt(0);
String left = s.substring(0, i);
String right = s.substring(i);
for (String u : interleave(right, t.substring(1))) {
result.add(left + c + u);
}
}
}
return result;
}
/**
* Prints some example interleavings to stdout.
*/
public static void main(String[] args) {
System.out.println(interleave("", ""));
System.out.println(interleave("a", ""));
System.out.println(interleave("", "1"));
System.out.println(interleave("a", "1"));
System.out.println(interleave("ab", "1"));
System.out.println(interleave("ab", "12"));
System.out.println(interleave("abc", "12"));
System.out.println(interleave("ab", "1234"));
}
}
If I interpreted your question correctly - that you want all the permutations of all the characters in both strings, then the following code will help. You will need to write your own swap function, and somehow obtain an array of all the characters in both strings.
This algorithm will permute from the i'th element up to the n'th element in the array. It is in C++, I would include a reference to where the algorithm is from but I can't remember.
void getPermutationsR(char characters[], int n, int i)
{
if (i == n)
{
//Output the current permutation
}
else
{
for (int j=i; j<n; j++)
{
swap (characters, i, j);
getPermutationsR(characters, n, i+1);
swap (characters, i, j);
}
}
}
What you have now is a good start. The problem is that it returns just one string, instead a list of those.
Change your function to return a list of string, and then think about how you could combine several lists to produce all the output you want.
Here is a solution using recursive approach, easy to understand too
public class Interleave {
public static List<String> interleave(String first, String second){
if(first.length() == 0){
List<String> list = new ArrayList<String>();
list.add(second);
return list;
}
else if(second.length() == 0){
List<String> list = new ArrayList<String>();
list.add(first);
return list;
}
else{
char c1 = first.charAt(0);
List<String> listA = multiply(c1,interleave(first.substring(1),second));
char c2 = second.charAt(0);
List<String> listB = multiply(c2,interleave(first,second.substring(1)));
listA.addAll(listB);
return listA;
}
}
public static List<String> multiply(char c,List<String> list){
List<String> result = new ArrayList<String>();
for(String str : list){
String res = Character.toString(c) + str;
result.add(res);
}
return result;
}
public static void main(String[] args){
System.out.println(interleave("ab", "1234"));
System.out.println(interleave("a", "b"));
System.out.println(interleave("ab", "cd"));
}
}
Below is the much better and simple to understand solution for this problem:
public class Interleaver {
/**
* Returns a list containing all possible interleavings of two strings.
* The order of the characters within the strings is preserved.
*/
public static String s1 = "abc";
public static String s2 = "12";
public static void interleave(int i, int j, String s) {
if (i == s1.length() && j == s2.length()) {
System.out.println("" + s);
}
if (i != s1.length()) {
interleave(i + 1, j, s + s1.charAt(i));
}
if (j != s2.length()) {
interleave(i, j + 1, s + s2.charAt(j));
}
}//Method ends here
/**
* Prints some example interleavings to stdout.
*/
public static void main(String[] args) {
interleave(0, 0, "");
}//Method ends here
}//Class ends here
Program is using just simple recursive calls to find the solution.
Here is another recursive solution:
public class Interleaving2 {
public static void main(String[] args) {
String x = "ab";
String y = "CD";
int m = x.length();
int n = y.length();
char[] result = new char[m + n + 1];
interleave(x, y, result, m, n, 0);
}
public static void interleave(String x, String y, char[] result, int m, int n, int i) {
if (m == 0 && n == 0) {
System.out.println(String.valueOf(result));
}
if (m != 0) {
result[i] = x.charAt(0);
interleave(x.substring(1), y, result, m - 1, n, i + 1);
}
if (n != 0) {
result[i] = y.charAt(0);
interleave(x, y.substring(1), result, m, n - 1, i + 1);
}
}
}

Categories

Resources