Shortest Palindrome with recursive solution issue - java

Debugging the following problem (a recursive solution) and confused what is the logical meaning of the for loop. If anyone have any insights, appreciated for sharing.
Given a string S, you are allowed to convert it to a palindrome by adding characters in front of it. Find and return the shortest palindrome you can find by performing this transformation.
For example:
Given "aacecaaa", return "aaacecaaa".
Given "abcd", return "dcbabcd".
int j = 0;
for (int i = s.length() - 1; i >= 0; i--) {
if (s.charAt(i) == s.charAt(j)) { j += 1; }
}
if (j == s.length()) { return s; }
String suffix = s.substring(j);
return new StringBuffer(suffix).reverse().toString() + shortestPalindrome(s.substring(0, j)) + suffix;
KMP based solution,
public class Solution {
public String shortestPalindrome(String s) {
String p = new StringBuffer(s).reverse().toString();
char pp[] = p.toCharArray();
char ss[] = s.toCharArray();
int m = ss.length;
if (m == 0) return "";
// trying to find the greatest overlap of pp[] and ss[]
// using the buildLPS() method of KMP
int lps[] = buildLPS(ss);
int i=0;// points to pp[]
int len = 0; //points to ss[]
while(i<m) {
if (pp[i] == ss[len]) {
i++;
len++;
if (i == m)
break;
} else {
if (len == 0) {
i++;
} else {
len = lps[len-1];
}
}
}
// after the loop, len is the overlap of the suffix of pp and prefix of ss
return new String(pp) + s.substring(len, m);
}
int [] buildLPS(char ss[]) {
int m = ss.length;
int lps[] = new int[m];
int len = 0;
int i = 1;
lps[0] = 0;
while(i < m) {
if (ss[i] == ss[len]) {
len++;
lps[i] = len;
i++;
} else {
if (len == 0) {
i++;
} else {
len = lps[len-1];
}
}
}
return lps;
}
}
thanks in advance,
Lin

My original comment was incorrect - as you've pointed out, in addition to using j'to check if s is a complete Palindrome, j is also used to find (intelligently guess?) the index around which to wrap + reverse the trailing characters from the longest palindrome which might exist at the beginning of the string. My understanding of the algorithm is as follows:
e.g. aacecaaa gives j = 7, resulting in
`aacecaaa` is `aacecaa` (palindrome) + `a` (suffix)
so the shortest palindrome appending to the start is:
`a` (suffix) + `aacecaa` + `a` (suffix)
Where the suffix consists of more than one character it must be reversed:
`aacecaaab` is `aacecaa` (palindrome) + `ab` (suffix)
So the solution in this case would be:
`ba` + `aacecaa` + `ab` (suffix)
In the worst case scenario j = 1 (since a will match when i=0 and j=0), e.g. abcd has no palindrome sequence in it, so the best which can be done is to wrap around the first character
dcb + a + bcd
To be honest, I'm not 100% confident that the algorithm you are debugging will work correctly in all cases but can't seem to find an a failed test case. The algorithm is certainly not intuitive.
Edit
I believe the shortest Palindrome can be derived deterministically, without the need for recursion at all - it seems that in the algorithm you are debugging, the recursion masks a side effect in the value of j. In my opinion, here's a way to determine j in a more intuitive manner:
private static String shortestPalindrome(String s) {
int j = s.length();
while (!isPalindrome(s.substring(0, j))) {
j--;
}
String suffix = s.substring(j);
// Similar to OP's original code, excluding the recursion.
return new StringBuilder(suffix).reverse()
.append(s.substring(0, j))
.append(suffix)
.toString();
}
I've pasted some test cases with an implementation of isPalindrome on Ideone here

public String shortestPalindrome(String s) {
String returnString ="";
int h = s.length()-1;
if(checkPalindrome(s))
{
return s;
}
while(h>=0)
{
returnString =returnString + s.charAt(h);
if(checkPalindrome(returnString+s))
{
return returnString+s;
}
h--;
}
return returnString+s;
}
public boolean checkPalindrome(String s)
{
int midpoint = s.length()/2;
// If the string length is odd, we do not need to check the central character
// as it is common to both
return (new StringBuilder(s.substring(0, midpoint)).reverse().toString()
.equals(s.substring(s.length() - midpoint)));
}

Related

Java: Find the longest substring without any number and at least one upper case character

Came across a programming exercise and was stuck. The problem is:
You need to define a valid password for an email but the only
restrictions are:
The password must contain one uppercase character
The password should not have numeric digit
Now, given a String, find the length of the longest substring which
is a valid password. For e.g Input Str = "a0Ba" , the output should
be 2 as "Ba" is the valid substring.
I used the concept of longest substring without repeating characters which I already did before but was unable to modify it to find the solution to above problem. My code for longest substring without repeating characters is:
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set<Character> set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {
// try to extend the range [i, j]
if (!set.contains(s.charAt(j))){
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);
}
else {
set.remove(s.charAt(i++));
}
}
return ans;
}
How about
final String input = "a0Ba";
final int answer = Arrays.stream(input.split("[0-9]+"))
.filter(s -> s.matches("(.+)?[A-Z](.+)?"))
.sorted((s1, s2) -> s2.length() - s1.length())
.findFirst()
.orElse("")
.length();
out.println(answer);
Arrays.stream(input.split("[0-9]+")) splits the original string into an array of strings. The separator is any sequence of numbers (numbers aren't allowed so they serve as separators). Then, a stream is created so I can apply functional operations and transformations.
.filter(s -> s.matches("(.+)?[A-Z](.+)?")) keeps into the stream only strings that have at least one upper-case letter.
.sorted((s1, s2) -> s2.length() - s1.length()) sorts the stream by length (desc).
.findFirst() tries to get the first string of the stream.
.orElse("") returns an empty string if no string was found.
.length(); gets the length of the string.
I suggest that you split your String to have an array of strings without digit:
yourString.split("[0-9]")
Then iterate over this array (says array a) to get the longest string that contains one Upper case character:
a[i].matches("[a-z]*[A-Z]{1}[a-z]*");
You can use a simple array. The algorithm to use would be a dynamic sliding window. Here is an example of a static sliding window: What is a Sliding Window
The algorithm should be as follows:
Keep track of 2 indexes of the array of char. These 2 indexes will be referred to as front and back here, representing the front and back of the array.
Have an int (I'll name it up here) to keep track of the number of upper case char.
Set all to 0.
Use a while loop that terminates if front > N where N is the number of char given.
If the next char is not a number, add 1 to front. Then check if that char is upper case. If so, add 1 to up.
If up is at least 1, update the maximum length if necessary.
If the next char is a number, continue checking the following char if they are also numbers. Set front to the first index where the char is not a number and back to front-1.
Output the maximum length.
You can use my solution which runs in O(n) time and finds the longest part without any digit and with a capital letter:
String testString = "skjssldfkjsakdfjlskdssfkjslakdfiop7adfaijsldifjasdjfil8klsasdfŞdijpfjapodifjpoaidjfpoaidjpfi9a";
int startIndex = 0;
int longestStartIndex = 0;
int endIndex = 0;
int index = 0;
int longestLength = Integer.MIN_VALUE;
boolean foundUpperCase = false;
while(index <= testString.length()) {
if (index == testString.length() || Character.isDigit(testString.charAt(index))) {
if (foundUpperCase && index > startIndex && index - startIndex > longestLength) {
longestLength = index - startIndex;
endIndex = index;
longestStartIndex = startIndex;
}
startIndex = index + 1;
foundUpperCase = false;
} else if (Character.isUpperCase(testString.charAt(index))) {
foundUpperCase = true;
}
index++;
}
System.out.println(testString.substring(longestStartIndex, endIndex));
You don't need regular expressions. Just use a few integers to act as index pointers into the string:
int i = 0;
int longestStart = 0;
int longestEnd = 0;
while (i < s.length()) {
// Skip past all the digits.
while (i < s.length() && Character.isDigit(s.charAt(i))) {
++i;
}
// i now points to the start of a substring
// or one past the end of the string.
int start = i;
// Keep a flag to record if there is an uppercase character.
boolean hasUppercase = false;
// Increment i until you hit another digit or the end of the string.
while (i < s.length() && !Character.isDigit(s.charAt(i))) {
hasUppercase |= Character.isUpperCase(s.charAt(i));
++i;
}
// Check if this is longer than the longest so far.
if (hasUppercase && i - start > longestEnd - longestStart) {
longestEnd = i;
longestStart = start;
}
}
String longest = s.substring(longestStart, longestEnd);
Ideone demo
Whilst more verbose than regular expressions, this has the advantage of not creating any unnecessary objects: the only object created is the longest string, right at the end.
I am using modification of Kadane algorithm to search the required password length. You may use isNumeric() and isCaps() function or include inline if statements. I have shown below with functions.
public boolean isNumeric(char x){
return (x>='0'&&x<='9');
}
public boolean isCaps(char x){
return (x>='A'&&x<='Z');
}
public int maxValidPassLen(String a)
{
int max_so_far = 0, max_ending_here = 0;
boolean cFlag = false;
int max_len = 0;
for (int i = 0; i < a.length(); i++)
{
max_ending_here = max_ending_here + 1;
if (isCaps(a.charAt(i))){
cFlag = true;
}
if (isNumeric(a.charAt(i))){
max_ending_here = 0;
cFlag = false;
}
else if (max_so_far<max_ending_here){
max_so_far = max_ending_here;
}
if(cFlag&&max_len<max_so_far){
max_len = max_so_far;
}
}
return max_len;
}
Hope this helps.
There are plenty of good answers here but thought it might be of interest to add one that uses Java 8 streams:
IntStream.range(0, s.length()).boxed()
.flatMap(b -> IntStream.range(b + 1, s.length())
.mapToObj(e -> s.substring(b, e)))
.filter(t -> t.codePoints().noneMatch(Character::isDigit))
.filter(t -> t.codePoints().filter(Character::isUpperCase).count() == 1)
.mapToInt(String::length).max();
If you wanted the string (rather than just the length), then the last line can be replaced with:
.max(Comparator.comparingInt(String::length));
Which returns an Optional<String>.
I'd use Streams and Optionals:
public static String getBestPassword(String password) throws Exception {
if (password == null) {
throw new Exception("Invalid password");
}
Optional<String> bestPassword = Stream.of(password.split("[0-9]"))
.filter(TypeErasure::containsCapital)
.sorted((o1, o2) -> o1.length() > o2.length() ? 1 : 0)
.findFirst();
if (bestPassword.isPresent()) {
return bestPassword.get();
} else {
throw new Exception("No valid password");
}
}
/**
* Returns true if word contains capital
*/
private static boolean containsCapital(String word) {
return word.chars().anyMatch(Character::isUpperCase);
}
Be sure to write some unit tests
public String pass(String str){
int length = 0;
boolean uppercase = false;
String s= "";
String d= "";
for(int i=0;i<str.length();i++){
if(Character.isUpperCase(str.charAt(i)) == true){
uppercase = true;
s = s+str.charAt(i);
}else if(Character.isDigit(str.charAt(i)) == true ){
if(uppercase == true && s.length()>length){
d = s;
s = "";
length = s.length();
uppercase = false;
}
}else if(i==str.length()-1&&Character.isDigit(str.charAt(i))==false){
s = s + str.charAt(i);
if(uppercase == true && s.length()>length){
d = s;
s = "";
length = s.length();
uppercase = false;
}
}else{
s = s+str.charAt(i);
}
}
return d;}
Here is a simple solution with Scala
def solution(str: String): Int = {
val strNoDigit = str.replaceAll("[0-9]", "-")
strAlphas = strNoDigit.split("-")
Try(strAlphas.filter(_.trim.find(_.isUpper).isDefined).maxBy(_.size))
.toOption
.map(_.length)
.getOrElse(-1)
}
Another solution using tail recursion in Scala
def solution2(str: String): Int = {
val subSt = new ListBuffer[Char]
def checker(str: String): Unit = {
if (str.nonEmpty) {
val s = str.head
if (!s.isDigit) {
subSt += s
} else {
subSt += '-'
}
checker(str.tail)
}
}
checker(str)
if (subSt.nonEmpty) {
val noDigitStr = subSt.mkString.split("-")
Try(noDigitStr.filter(s => s.nonEmpty && s.find(_.isUpper).isDefined).maxBy(_.size))
.toOption
.map(_.length)
.getOrElse(-1)
} else {
-1
}
}
This is a dynamic programming problem. You can solve this yourself using a matrix. It is easy enough. Just give it a try. Take the characters of the password as the rows and columns of the matrix. Add the diagonals if the current character appended to the last character forms a valid password. Start with the smallest valid password as the initial condition.
String[] s = testString.split("[0-9]");
int length = 0;
int index = -1;
for(int i=0; i< s.length; i++){
if(s[i].matches("[a-z]*.*[A-Z].*[a-z]*")){
if(length <= s[i].length()){
length = s[i].length();
index = i;
}
}
}
if(index >= 0){
System.out.println(s[index]);
}
//easiest way to do it:
String str = "a0Ba12hgKil8oPlk";
String[] str1 = str.split("[0-9]+");
List<Integer> in = new ArrayList<Integer>();
for (int i = 0; i < str1.length; i++) {
if (str1[i].matches("(.+)?[A-Z](.+)?")) {
in.add(str1[i].length());
} else {
System.out.println(-1);
}
}
Collections.sort(in);
System.out.println("string : " + in.get(in.size() - 1));
This is my solution with c#. I tested a range of strings and it gave me the correct value. Used Split. No Regex or Substrings. Let me know if it works; open to improvements and corrections.
public static int validPassword(string str)
{
List<int> strLength = new List<int>();
if (!(str.All(Char.IsDigit)))
{
//string str = "a0Bb";
string[] splitStrs = str.Split(new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
//check if each string contains a upper case
foreach (string s in splitStrs)
{
//Console.WriteLine(s);
if (s.Any(char.IsUpper) && s.Any(char.IsLower) || s.Any(char.IsUpper))
{
strLength.Add(s.Length);
}
}
if (strLength.Count == 0)
{
return -1;
}
foreach (int i in strLength)
{
//Console.WriteLine(i);
}
return strLength.Max();
}
else
{
return -1;
}
}
I think this solution takes care of all the possible corner cases. It passed all the test cases in an Online Judge. It is a dynamic sliding window O(n) solution.
public class LongestString {
public static void main(String[] args) {
// String testString = "AabcdDefghIjKL0";
String testString = "a0bb";
int startIndex = 0, endIndex = 0;
int previousUpperCaseIndex = -1;
int maxLen = 0;
for (; endIndex < testString.length(); endIndex++) {
if (Character.isUpperCase(testString.charAt(endIndex))) {
if (previousUpperCaseIndex > -1) {
maxLen = Math.max(maxLen, endIndex - startIndex);
startIndex = previousUpperCaseIndex + 1;
}
previousUpperCaseIndex = endIndex;
} else if (Character.isDigit(testString.charAt(endIndex))) {
if (previousUpperCaseIndex > -1) {
maxLen = Math.max(maxLen, endIndex - startIndex);
}
startIndex = endIndex + 1;
previousUpperCaseIndex = -1;
}
}
if (previousUpperCaseIndex > -1)
maxLen = Math.max(maxLen, endIndex - startIndex);
System.out.println(maxLen);
}}
function ValidatePassword(password){
var doesContainNumber = false;
var hasUpperCase = false;
for(var i=0;i<password.length;i++){
if(!isNaN(password[i]))
doesContainNumber = true;
if(password[i] == password[i].toUpperCase())
hasUpperCase = true;
}
if(!doesContainNumber && hasUpperCase)
return true;
else
return false;
}
function GetLongestPassword(inputString){
var longestPassword = "";
for(var i=0;i<inputString.length-1;i++)
{
for (var j=i+1;j<inputString.length;j++)
{
var substring = inputString.substring(i,j+1);
var isValid = ValidatePassword(substring);
if(isValid){
if(substring.length > longestPassword.length)
{
longestPassword = substring;
}
}
}
}
if(longestPassword == "")
{
return "No Valid Password found";
}
else
{
return longestPassword;
}
}

Make The All of Combination from Palindrome

I have a problem for my task. I must make a program that the input is a palindrome / not a palindrome, and the output is return the substring of the string that can be a palindrome in recursive. Example :
"marah" , the output should be, ("m","a","r","a","h") , ("m","ara","h") . I dont know to implement this in recursive. Please anyone who can help me, i'm very need that code. I worked it in java. Thank you, i hope there is a help coming :D .
public static String palindrome(String s) {
String s, sub;
int i, c, length;
Scanner in = new Scanner(System.in);
System.out.println("Enter a string to print it's all substrings");
s = in.nextLine();
length = string.length();
System.out.println("Substrings of \"" + string + "\" are :-");
for (c = 0; c < length; c++) {
for (i = 1; i <= length - c; i++) {
sub = string.substring(c, c + i);
System.out.println(sub);
}
}
}
public static String longestPalindrome(String s) {
if (s.isEmpty()) {
return null;
}
if (s.length() == 1) {
return s;
}
String longest = s.substring(0, 1);
for (int i = 0; i < s.length(); i++) {
// get longest palindrome with center of i
String tmp = helper(s, i, i);
if (tmp.length() > longest.length()) {
longest = tmp;
}
// get longest palindrome with center of i, i+1
tmp = helper(s, i, i + 1);
if (tmp.length() > longest.length()) {
longest = tmp;
}
}
return longest;
}
// Given a center, either one letter or two letter,
// Find longest palindrome
public static String helper(String s, int begin, int end) {
while (begin >= 0 && end <= s.length() - 1 && s.charAt(begin) == s.charAt(end)) {
begin--;
end++;
}
return s.substring(begin + 1, end);
}
if the input is "mama", the output is only "ama", the expected is, "m","a","m","a" , "mam","a" , and "m","ama" . Anybody can help?
This is called palindrom partition, you can find it here http://www.programcreek.com/2013/03/leetcode-palindrome-partitioning-java/

Converting a string into a palindrome in Java

I'm trying to find the shortest palindrome that one can create from S by by adding 0 or more characters in front of it. For example the shortest palindrome can be constructed from 'baaa' is 'aaabaaa'. The two functions that I'm using are given below. This works for this case for doesn't yield the shortest result in all cases.
public static boolean checkPalindrome(String s){
for (int i = 0; i < s.length()/2; i++) {
if (s.charAt(i) != s.charAt(s.length() - i - 1)) return false;
}
return true;
}
public static int makePalindrome(String s){
int min = 0;
StringBuilder str = new StringBuilder(s);
for (int i = 1; i < s.length() ; i++) {
str.insert(0, s.charAt(i));
if (checkPalindrome(str.toString()) == true) {
min = str.length();
break;
}
}
return min;
}
I can't seem to figure out what logical step am I missing.
Your helper method checkPalindrome seems correct. Your thinking is also correct (append characters until the result is a palindrome), but the way you're going about it is wrong.
To reiterate: Our logic is, while our result is not a palindrome, take the next character from the end (moving towards the start of the string) and append it to the prefix. So for the string "abcd", we would try
"" + "abcd" -> "abcd" -> false
"d" + "abcd" -> "dabcd" -> false
"dc" + "abcd" -> "dcabcd" -> false
"dcb" + "abcd" -> "dcbabcd" -> true, terminate
Here's a fixed version:
public static String makePalindrome(String base){
String pref = "";
int i = base.length() - 1;
while(! checkPalindrome(pref + base)){
pref = pref + base.charAt(i);
i --;
}
return pref + base;
}
I am not sure i understand the question but from what i understood you want to turn Strings like Hello to olleHello To do this, loop trhough each char of the string with like:
String example = "Hello There Mate"; //our string
StringBuilder exampleBuilder = new StringBuilder();
for(int i=example.length()-1; i>0; i--)
exampleBuilder.append(example.charAt(i));
//Our loop, the reason why it is example.lenght-1
//is because we want the first letter to appear only
//1 time :)
String finalString = exampleBuilder.toString()+example;
//The final string, should be 'olleHello'
System.out.println(finalString);
Hope thats what you are looking for :D
IDEONE: http://ideone.com/tawjmG
We can find the shortest Palindrome using the following logic:
Find the midpoint, loop from 0 to midpoint, length-1 to midpoint.
If palindrome, return
If not palindrome, add 1 to midpoint, do same logic
In code:
static String shortestPalindrome(String s) {
if (s.length() == 1) return s;
return recShortestPalindrome(s, s.length()>>1, 0);
}
static String recShortestPalindrome(String s, int mid, int add) {
// AABBA[X]
int fakeLen = s.length() + add;
for (int i = 0; i < mid; i++) {
char c1 = s.charAt(i);
int p1 = fakeLen - 1 - i;
if (p1 < s.length()) {
char c2 = s.charAt(p1);
if (c2 != c1) {
return recShortestPalindrome(s, mid+1, add+1);
}
}
}
// found a pattern that works
String h1 = s.substring(0, mid);
String h2 = new StringBuilder(h1).reverse().toString();
String ret = h1+h2;
int midPoint = ret.length()/2;
if (ret.length()%2 == 0 && ret.length() >= 2) {
char c1 = ret.charAt(midPoint);
char c2 = ret.charAt(midPoint-1);
if (c1 == c2) {
return ret.substring(0, midPoint) + ret.substring(midPoint+1, ret.length());
}
}
return h1+h2;
}
Python Solution:
def isPalindrome(x):
if x == "":
return False
r = ""
r = str(x)
r = r[::-1]
return True if x == r else False
def makePalindrome(my_str):
pref = ""
i = len(my_str) - 1
while isPalindrome(pref + my_str) == False:
pref = pref + my_str[i]
i -= 1
return pref + my_str
my_str = "abcd"
print(makePalindrome(my_str))

Recursively swap pairs of letters in a string in java

For example, if I call exchangePairs("abcdefg"), I should receive "badcfeg" in return.
This is for a homework assignment, any kind of pseudocode would be very helpful. I am just beginning to learn recursion and up until this problem I haven't had too much of an issue.
public String swapPairs(String s) {
if (s.length() < 2)
return s;
else
return swap(s.charAt(0), s.charAt(1)) + swapPairs(s.substring(2));
}
You're not just beginning to learn recursion, because recursion is part of your everyday live. You just don't notice, because it is so normal and nobody calls it recursion.
For example, you watch a movie on TV, and in one scene there is someone watching a movie on TV.
In programming, recursion is a way to make hard things easy. Always start with the easy case:
What is the result of exchangePairs("")?
What is the result of exchangePairs("x") where x is any character?
Suppose you have already completed exchangePairs(), how would the result be for "xy..." where "..." is any string? Surely "yx+++", where "+++" is the result of exchangePairs("...").
Now, it turns out that we've covered all cases! Problem solved!
Such is the greatness of recursion. You just use your function as if it were complete despite you've not completed it yet.
Why use recursion?
for (int i = 0; i + 1 < strlen(str); ++i) {
char tmp = str[i + 1];
str[i + 1] = str[i];
str[i] = tmp;
}
If you have to use recursion, I suppose you could do something like this:
char* exchangePairs(char* str) {
if (strlen(str) >= 2) {
// if there are characters left, swap the first two, then recurse
char tmp = str[1];
str[1] = str[0];
str[0] = str[1];
exchangePairs(str + 2);
}
return str;
}
That's in C, but it should give you the idea (I'm better in C and didn't want to just give you a copy/pasteable solution).
Use tail recursion
String reverse(String input)
{
if(String.length()==1)
{
return input;
}
else
{
return reverse(input,"");
}
}
String reverse(String input, String result)
{
if(input.length == 0) return result;
else return result(input.substring(1),input.charAt(0) + result);
}
Ok Here is my solution. I dont have Java at my disposal so I did it in C# which is very similar to Java so should be easy to understand/port;
public static char[] exchangePairs(char[] charArray, int current)
{
if(current >= charArray.Length - 1)
{
return charArray;
}
char nextChar = charArray[current + 1];
char currentChar = charArray[current];
charArray[current] = nextChar;
charArray[current + 1] = currentChar;
int i = current + 2;
return exchangePairs(charArray, i);
}
Call to the method:
exchangePairs("abcdefghij".ToCharArray(), 0);
public static String swapPairs(String s) {
String even = "";
String odd = "";
int length = s.length();
for (int i = 0; i <= length-2; i+=2) {
even += s.charAt(i+1) + "" + s.charAt(i);
}
if (length % 2 != 0) {
odd = even + s.charAt(length-1);
return odd;
} else {
return even;
}
}
A small adding on Steven's solution, you can use StringBuffer/StringBuilder.reverse() for reversing a string.
public String swapPairs(String s) {
if (s.length() < 2)
return s;
else {
return new StringBuffer(s.substring(0, 2)).reverse().toString() + swapPairs(s.substring(2));
}
}
I'd introduce an integer recursion control variable which is how much of the string has already been exchanged. At each level, check the control variable to see if there's more to do and, if so, exchange the next pair, increment by 2, and recurse.

function to remove duplicate characters in a string

The following code is trying to remove any duplicate characters in a string. I'm not sure if the code is right. Can anybody help me work with the code (i.e whats actually happening when there is a match in characters)?
public static void removeDuplicates(char[] str) {
if (str == null) return;
int len = str.length;
if (len < 2) return;
int tail = 1;
for (int i = 1; i < len; ++i) {
int j;
for (j = 0; j < tail; ++j) {
if (str[i] == str[j]) break;
}
if (j == tail) {
str[tail] = str[i];
++tail;
}
}
str[tail] = 0;
}
The function looks fine to me. I've written inline comments. Hope it helps:
// function takes a char array as input.
// modifies it to remove duplicates and adds a 0 to mark the end
// of the unique chars in the array.
public static void removeDuplicates(char[] str) {
if (str == null) return; // if the array does not exist..nothing to do return.
int len = str.length; // get the array length.
if (len < 2) return; // if its less than 2..can't have duplicates..return.
int tail = 1; // number of unique char in the array.
// start at 2nd char and go till the end of the array.
for (int i = 1; i < len; ++i) {
int j;
// for every char in outer loop check if that char is already seen.
// char in [0,tail) are all unique.
for (j = 0; j < tail; ++j) {
if (str[i] == str[j]) break; // break if we find duplicate.
}
// if j reachs tail..we did not break, which implies this char at pos i
// is not a duplicate. So we need to add it our "unique char list"
// we add it to the end, that is at pos tail.
if (j == tail) {
str[tail] = str[i]; // add
++tail; // increment tail...[0,tail) is still "unique char list"
}
}
str[tail] = 0; // add a 0 at the end to mark the end of the unique char.
}
Your code is, I'm sorry to say, very C-like.
A Java String is not a char[]. You say you want to remove duplicates from a String, but you take a char[] instead.
Is this char[] \0-terminated? Doesn't look like it because you take the whole .length of the array. But then your algorithm tries to \0-terminate a portion of the array. What happens if the arrays contains no duplicates?
Well, as it is written, your code actually throws an ArrayIndexOutOfBoundsException on the last line! There is no room for the \0 because all slots are used up!
You can add a check not to add \0 in this exceptional case, but then how are you planning to use this code anyway? Are you planning to have a strlen-like function to find the first \0 in the array? And what happens if there isn't any? (due to all-unique exceptional case above?).
What happens if the original String/char[] contains a \0? (which is perfectly legal in Java, by the way, see JLS 10.9 An Array of Characters is Not a String)
The result will be a mess, and all because you want to do everything C-like, and in place without any additional buffer. Are you sure you really need to do this? Why not work with String, indexOf, lastIndexOf, replace, and all the higher-level API of String? Is it provably too slow, or do you only suspect that it is?
"Premature optimization is the root of all evils". I'm sorry but if you can't even understand what the original code does, then figuring out how it will fit in the bigger (and messier) system will be a nightmare.
My minimal suggestion is to do the following:
Make the function takes and returns a String, i.e. public static String removeDuplicates(String in)
Internally, works with char[] str = in.toCharArray();
Replace the last line by return new String(str, 0, tail);
This does use additional buffers, but at least the interface to the rest of the system is much cleaner.
Alternatively, you can use StringBuilder as such:
static String removeDuplicates(String s) {
StringBuilder noDupes = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
String si = s.substring(i, i + 1);
if (noDupes.indexOf(si) == -1) {
noDupes.append(si);
}
}
return noDupes.toString();
}
Note that this is essentially the same algorithm as what you had, but much cleaner and without as many little corner cases, etc.
Given the following question :
Write code to remove the duplicate characters in a string without
using any additional buffer. NOTE: One or two additional variables
are fine. An extra copy of the array is not.
Since one or two additional variables are fine but no buffer is allowed, you can simulate the behaviour of a hashmap by using an integer to store bits instead. This simple solution runs at O(n), which is faster than yours. Also, it isn't conceptually complicated and in-place :
public static void removeDuplicates(char[] str) {
int map = 0;
for (int i = 0; i < str.length; i++) {
if ((map & (1 << (str[i] - 'a'))) > 0) // duplicate detected
str[i] = 0;
else // add unique char as a bit '1' to the map
map |= 1 << (str[i] - 'a');
}
}
The drawback is that the duplicates (which are replaced with 0's) will not be placed at the end of the str[] array. However, this can easily be fixed by looping through the array one last time. Also, an integer has the capacity for only regular letters.
private static String removeDuplicateCharactersFromWord(String word) {
String result = new String("");
for (int i = 0; i < word.length(); i++) {
if (!result.contains("" + word.charAt(i))) {
result += "" + word.charAt(i);
}
}
return result;
}
This is my solution.
The algorithm is mainly the same as the one in the book "Cracking the code interview" where this exercise comes from, but I tried to improve it a bit and make the code more understandable:
public static void removeDuplicates(char[] str) {
// if string has less than 2 characters, it can't contain
// duplicate values, so there's nothing to do
if (str == null || str.length < 2) {
return;
}
// variable which indicates the end of the part of the string
// which is 'cleaned' (all duplicates removed)
int tail = 0;
for (int i = 0; i < str.length; i++) {
boolean found = false;
// check if character is already present in
// the part of the array before the current char
for (int j = 0; j < i; j++) {
if (str[j] == str[i]) {
found = true;
break;
}
}
// if char is already present
// skip this one and do not copy it
if (found) {
continue;
}
// copy the current char to the index
// after the last known unique char in the array
str[tail] = str[i];
tail++;
}
str[tail] = '\0';
}
One of the important requirements from the book is to do it in-place (as in my solution), which means that no additional data structure should be used as a helper while processing the string. This improves performance by not wasting memory unnecessarily.
char[] chars = s.toCharArray();
HashSet<Character> charz = new HashSet<Character>();
for(Character c : s.toCharArray() )
{
if(!charz.contains(c))
{
charz.add(c);
//System.out.print(c);
}
}
for(Character c : charz)
{
System.out.print(c);
}
public String removeDuplicateChar(String nonUniqueString) {
String uniqueString = "";
for (char currentChar : nonUniqueString.toCharArray()) {
if (!uniqueString.contains("" + currentChar)) {
uniqueString += currentChar;
}
}
return uniqueString;
}
public static void main (String [] args)
{
String s = "aabbbeeddsfre";//sample string
String temp2="";//string with no duplicates
HashMap<Integer,Character> tc = new HashMap<Integer,Character>();//create a hashmap to store the char's
char [] charArray = s.toCharArray();
for (Character c : charArray)//for each char
{
if (!tc.containsValue(c))//if the char is not already in the hashmap
{
temp2=temp2+c.toString();//add the char to the output string
tc.put(c.hashCode(),c);//and add the char to the hashmap
}
}
System.out.println(temp2);//final string
}
instead of HashMap I think we can use Set too.
I understand that this is a Java question, but since I have a nice solution which could inspire someone to convert this into Java, by all means. Also I like answers where multiple language submissions are available to common problems.
So here is a Python solution which is O(n) and also supports the whole ASCII range. Of course it does not treat 'a' and 'A' as the same:
I am using 8 x 32 bits as the hashmap:
Also input is a string array using dedup(list('some string'))
def dedup(str):
map = [0,0,0,0,0,0,0,0]
for i in range(len(str)):
ascii = ord(str[i])
slot = ascii / 32
bit = ascii % 32
bitOn = map[slot] & (1 << bit)
if bitOn:
str[i] = ''
else:
map[slot] |= 1 << bit
return ''.join(str)
also a more pythonian way to do this is by using a set:
def dedup(s):
return ''.join(list(set(s)))
Substringing method. Concatenation is done with .concat() to avoid allocation additional memory for left hand and right hand of +.
Note: This removes even duplicate spaces.
private static String withoutDuplicatesSubstringing(String s){
for(int i = 0; i < s.length(); i++){
String sub = s.substring(i+1);
int index = -1;
while((index = sub.toLowerCase().indexOf(Character.toLowerCase(s.charAt(i)))) > -1 && !sub.isEmpty()){
sub = sub.substring(0, index).concat(sub.substring(index+1, sub.length()));
}
s = s.substring(0, i+1).concat(sub);
}
return s;
}
Test case:
String testCase1 = "nanananaa! baaaaatmaan! batman!";
Output:
na! btm
Question: Remove Duplicate characters in a string
Method 1 :(Python)
import collections
a = "GiniGinaProtijayi"
aa = collections.OrderedDict().fromkeys(a)
print(''.join(aa))
Method 2 :(Python)
a = "GiniGinaProtijayi"
list = []
aa = [ list.append(ch) for ch in a if ch not in list]
print( ''.join(list))
IN Java:
class test2{
public static void main(String[] args) {
String a = "GiniGinaProtijayi";
List<Character> list = new ArrayList<>();
for(int i = 0 ; i < a.length() ;i++) {
char ch = a.charAt(i);
if( list.size() == 0 ) {list.add(ch);}
if(!list.contains(ch)) {list.add(ch) ;}
}//for
StringBuffer sbr = new StringBuffer();
for( char ch : list) {sbr.append(ch);}
System.out.println(sbr);
}//main
}//end
This would be much easier if you just looped through the array and added all new characters to a list, then retruned that list.
With this approach, you need to reshuffle the array as you step through it and eventually redimension it to the appropriate size in the end.
String s = "Javajk";
List<Character> charz = new ArrayList<Character>();
for (Character c : s.toCharArray()) {
if (!(charz.contains(Character.toUpperCase(c)) || charz
.contains(Character.toLowerCase(c)))) {
charz.add(c);
}
}
ListIterator litr = charz.listIterator();
while (litr.hasNext()) {
Object element = litr.next();
System.err.println(":" + element);
} }
this will remove the duplicate if the character present in both the case.
public class RemoveDuplicateInString {
public static void main(String[] args) {
String s = "ABCDDCA";
RemoveDuplicateInString rs = new RemoveDuplicateInString();
System.out.println(rs.removeDuplicate(s));
}
public String removeDuplicate(String s) {
String retn = null;
boolean[] b = new boolean[256];
char[] ch = s.toCharArray();
for (int i = 0; i < ch.length; i++) {
if (b[ch[i]]) {
ch[i]=' ';
}
else {
b[ch[i]] = true;
}
}
retn = new String(ch);
return retn;
}
}
/* program to remove the duplicate character in string */
/* Author senthilkumar M*/
char *dup_remove(char *str)
{
int i = 0, j = 0, l = strlen(str);
int flag = 0, result = 0;
for(i = 0; i < l; i++) {
result = str[i] - 'a';
if(flag & (1 << result)) {
*/* if duplicate found remove & shift the array*/*
for(j = i; j < l; j++) {
str[j] = str[j+1];
}
i--;
l--; /* duplicates removed so string length reduced by 1 character*/
continue;
}
flag |= (1 << result);
}
return str;
}
public class RemoveCharsFromString {
static String testcase1 = "No, I am going to Noida";
static String testcase2 = "goings";
public static void main(String args[])throws StringIndexOutOfBoundsException{
RemoveCharsFromString testInstance= new RemoveCharsFromString();
String result = testInstance.remove(testcase1,testcase2);
System.out.println(result);
}
//write your code here
public String remove(String str, String str1)throws StringIndexOutOfBoundsException
{ String result=null;
if (str == null)
return "";
try
{
for (int i = 0; i < str1.length (); i++)
{
char ch1=str1.charAt(i);
for(int j=0;j<str.length();j++)
{
char ch = str.charAt (j);
if (ch == ch1)
{
String s4=String.valueOf(ch);
String s5= str.replaceAll(s4, "");
str=s5;
}
}
}
}
catch(Exception e)
{
}
result=str;
return result;
}
}
public static void main(String[] args) {
char[] str = { 'a', 'b', 'a','b','c','e','c' };
for (int i = 1; i < str.length; i++) {
for (int j = 0; j < i; j++) {
if (str[i] == str[j]) {
str[i] = ' ';
}
}
}
System.out.println(str);
}
An improved version for using bitmask to handle 256 chars:
public static void removeDuplicates3(char[] str)
{
long map[] = new long[] {0, 0, 0 ,0};
long one = 1;
for (int i = 0; i < str.length; i++)
{
long chBit = (one << (str[i]%64));
int n = (int) str[i]/64;
if ((map[n] & chBit ) > 0) // duplicate detected
str[i] = 0;
else // add unique char as a bit '1' to the map
map[n] |= chBit ;
}
// get rid of those '\0's
int wi = 1;
for (int i=1; i<str.length; i++)
{
if (str[i]!=0) str[wi++] = str[i];
}
// setting the rest as '\0'
for (;wi<str.length; wi++) str[wi] = 0;
}
Result: "##1!!ASDJasanwAaw.,;..][,[]==--0" ==> "#1!ASDJasnw.,;][=-0" (double quotes not included)
This function removes duplicate from string inline. I have used C# as a coding language and the duplicates are removed inline
public static void removeDuplicate(char[] inpStr)
{
if (inpStr == null) return;
if (inpStr.Length < 2) return;
for (int i = 0; i < inpStr.Length; ++i)
{
int j, k;
for (j = 1; j < inpStr.Length; j++)
{
if (inpStr[i] == inpStr[j] && i != j)
{
for (k = j; k < inpStr.Length - 1; k++)
{
inpStr[k] = inpStr[k + 1];
}
inpStr[k] = ' ';
}
}
}
Console.WriteLine(inpStr);
}
(Java) Avoiding usage of Map, List data structures:
private String getUniqueStr(String someStr) {
StringBuilder uniqueStr = new StringBuilder();
if(someStr != null) {
for(int i=0; i <someStr.length(); i++) {
if(uniqueStr.indexOf(String.valueOf(someStr.charAt(i))) == -1) {
uniqueStr.append(someStr.charAt(i));
}
}
}
return uniqueStr.toString();
}
package com.java.exercise;
public class RemoveCharacter {
/**
* #param args
*/
public static void main(String[] args) {
RemoveCharacter rem = new RemoveCharacter();
char[] ch=rem.GetDuplicates("JavavNNNNNNC".toCharArray());
char[] desiredString="JavavNNNNNNC".toCharArray();
System.out.println(rem.RemoveDuplicates(desiredString, ch));
}
char[] GetDuplicates(char[] input)
{
int ctr=0;
char[] charDupl=new char[20];
for (int i = 0; i <input.length; i++)
{
char tem=input[i];
for (int j= 0; j < i; j++)
{
if (tem == input[j])
{
charDupl[ctr++] = input[j];
}
}
}
return charDupl;
}
public char[] RemoveDuplicates(char[] input1, char []input2)
{
int coutn =0;
char[] out2 = new char[10];
boolean flag = false;
for (int i = 0; i < input1.length; i++)
{
for (int j = 0; j < input2.length; j++)
{
if (input1[i] == input2[j])
{
flag = false;
break;
}
else
{
flag = true;
}
}
if (flag)
{
out2[coutn++]=input1[i];
flag = false;
}
}
return out2;
}
}
Yet another solution, seems to be the most concise so far:
private static String removeDuplicates(String s)
{
String x = new String(s);
for(int i=0;i<x.length()-1;i++)
x = x.substring(0,i+1) + (x.substring(i+1)).replace(String.valueOf(x.charAt(i)), "");
return x;
}
I have written a piece of code to solve the problem.
I have checked with certain values, got the required output.
Note: It's time consuming.
static void removeDuplicate(String s) {
char s1[] = s.toCharArray();
Arrays.sort(s1); //Sorting is performed, a to z
//Since adjacent values are compared
int myLength = s1.length; //Length of the character array is stored here
int i = 0; //i refers to the position of original char array
int j = 0; //j refers to the position of char array after skipping the duplicate values
while(i != myLength-1 ){
if(s1[i]!=s1[i+1]){ //Compares two adjacent characters, if they are not the same
s1[j] = s1[i]; //if not same, then, first adjacent character is stored in s[j]
s1[j+1] = s1[i+1]; //Second adjacent character is stored in s[j+1]
j++; //j is incremented to move to next location
}
i++; //i is incremented
}
//the length of s is i. i>j
String s4 = new String (s1); //Char Array to String
//s4[0] to s4[j+1] contains the length characters after removing the duplicate
//s4[j+2] to s4[i] contains the last set of characters of the original char array
System.out.println(s4.substring(0, j+1));
}
Feel free to run my code with your inputs. Thanks.
public class RemoveRepeatedCharacters {
/**
* This method removes duplicates in a given string in one single pass.
* Keeping two indexes, go through all the elements and as long as subsequent characters match, keep
* moving the indexes in opposite directions. When subsequent characters don't match, copy value at higher index
* to (lower + 1) index.
* Time Complexity = O(n)
* Space = O(1)
*
*/
public static void removeDuplicateChars(String text) {
char[] ch = text.toCharArray();
int i = 0; //first index
for(int j = 1; j < ch.length; j++) {
while(i >= 0 && j < ch.length && ch[i] == ch[j]) {
i--;
j++;
System.out.println("i = " + i + " j = " + j);
}
if(j < ch.length) {
ch[++i] = ch[j];
}
}
//Print the final string
for(int k = 0; k <= i; k++)
System.out.print(ch[k]);
}
public static void main(String[] args) {
String text = "abccbdeefgg";
removeDuplicateChars(text);
}
}
public class StringRedundantChars {
/**
* #param args
*/
public static void main(String[] args) {
//initializing the string to be sorted
String sent = "I love painting and badminton";
//Translating the sentence into an array of characters
char[] chars = sent.toCharArray();
System.out.println("Before Sorting");
showLetters(chars);
//Sorting the characters based on the ASCI character code.
java.util.Arrays.sort(chars);
System.out.println("Post Sorting");
showLetters(chars);
System.out.println("Removing Duplicates");
stripDuplicateLetters(chars);
System.out.println("Post Removing Duplicates");
//Sorting to collect all unique characters
java.util.Arrays.sort(chars);
showLetters(chars);
}
/**
* This function prints all valid characters in a given array, except empty values
*
* #param chars Input set of characters to be displayed
*/
private static void showLetters(char[] chars) {
int i = 0;
//The following loop is to ignore all white spaces
while ('\0' == chars[i]) {
i++;
}
for (; i < chars.length; i++) {
System.out.print(" " + chars[i]);
}
System.out.println();
}
private static char[] stripDuplicateLetters(char[] chars) {
// Basic cursor that is used to traverse through the unique-characters
int cursor = 0;
// Probe which is used to traverse the string for redundant characters
int probe = 1;
for (; cursor < chars.length - 1;) {
// Checking if the cursor and probe indices contain the same
// characters
if (chars[cursor] == chars[probe]) {
System.out.println("Removing char : " + chars[probe]);
// Please feel free to replace the redundant character with
// character. I have used '\0'
chars[probe] = '\0';
// Pushing the probe to the next character
probe++;
} else {
// Since the probe has traversed the chars from cursor it means
// that there were no unique characters till probe.
// Hence set cursor to the probe value
cursor = probe;
// Push the probe to refer to the next character
probe++;
}
}
System.out.println();
return chars;
}
}
This is my solution
public static String removeDup(String inputString){
if (inputString.length()<2) return inputString;
if (inputString==null) return null;
char[] inputBuffer=inputString.toCharArray();
for (int i=0;i<inputBuffer.length;i++){
for (int j=i+1;j<inputBuffer.length;j++){
if (inputBuffer[i]==inputBuffer[j]){
inputBuffer[j]=0;
}
}
}
String result=new String(inputBuffer);
return result;
}
Well I came up with the following solution.
Keeping in mind that S and s are not duplicates. Also I have just one hard coded value.. But the code works absolutely fine.
public static String removeDuplicate(String str)
{
StringBuffer rev = new StringBuffer();
rev.append(str.charAt(0));
for(int i=0; i< str.length(); i++)
{
int flag = 0;
for(int j=0; j < rev.length(); j++)
{
if(str.charAt(i) == rev.charAt(j))
{
flag = 0;
break;
}
else
{
flag = 1;
}
}
if(flag == 1)
{
rev.append(str.charAt(i));
}
}
return rev.toString();
}
I couldn't understand the logic behind the solution so I wrote my simple solution:
public static void removeDuplicates(char[] str) {
if (str == null) return; //If the string is null return
int length = str.length; //Getting the length of the string
if (length < 2) return; //Return if the length is 1 or smaller
for(int i=0; i<length; i++){ //Loop through letters on the array
int j;
for(j=i+1;j<length;j++){ //Loop through letters after the checked letters (i)
if (str[j]==str[i]){ //If you find duplicates set it to 0
str[j]=0;
}
}
}
}
Using guava you can just do something like Sets.newHashSet(charArray).toArray();
If you are not using any libraries, you can still use new HashSet<Char>() and add your char array there.
#include <iostream>
#include <string>
using namespace std;
int main() {
// your code goes here
string str;
cin >> str;
long map = 0;
for(int i =0; i < str.length() ; i++){
if((map & (1L << str[i])) > 0){
str[i] = 0;
}
else{
map |= 1L << str[i];
}
}
cout << str;
return 0;
}

Categories