Problem description from school assignment
Longest String With Palindrome
I'm getting complexity O(N^2). How can I achieve O(N*log(N))**
My code
int maxL = 0;
for (int i = 0; i < S.length(); i++) {
String currentString = String.valueOf(S.charAt(i));
for (int j = i + 1; j < S.length(); j = j + 1) {
String jStr = String.valueOf(S.charAt(j));
if (currentString.contains(jStr)) {
currentString = currentString.replace(jStr, "");
int len = j - i + 1;
if (currentString.length() == 0 && maxL < len) {
maxL = len;
}
} else {
currentString = currentString + jStr;
}
}
}
return maxL;
This problem can be solved in O(n) time using O(n) space. The following algorithm uses a bit set to keep track of the unbalanced characters for the substrings starting at the beginning of the given string. It makes a single pass through the string and remembers the states it has already seen in a hash map. Whenever we see the same state a second time, we have found a valid password: just remove the old shorter substring from the beginning of the current substring.
private static int index(char c) {
if (c < '0') throw new IllegalArgumentException("illegal char");
if (c <= '9') return c - '0';
if (c < 'a') throw new IllegalArgumentException("illegal char");
if (c <= 'z') return c - 'a' + 10;
throw new IllegalArgumentException("illegal char");
}
private static int solution(String s) {
HashMap<BitSet, Integer> states = new HashMap<>();
int longest = 0;
BitSet state = new BitSet();
states.put((BitSet) state.clone(), 0);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
state.flip(index(c));
Integer seenAt = states.get(state);
if (seenAt != null) {
int len = i - seenAt + 1;
if (len > longest) longest = len;
} else {
states.put((BitSet) state.clone(), i + 1);
}
}
return longest;
}
Related
I'm trying to rearrange words in any given string (20 words or less). I'm having this issue because I can not get the last word in the string to print. I tried modifying the loop range, but I am not able to fix the issue.
public class ListString {
String[] list = new String[20];
int n = 0;
public void read() {
Scanner in = new Scanner(System.in);
System.out.println("Please enter the sentence");
String s = in.nextLine();
String temp = "";
for (int i = 0; i < s.length(); i++)
{
char ch = s.charAt(i);
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) // range from a to z
temp = temp + ch;
else
{
if (temp.length() > 0)
{
list[n] = temp;
n++;
temp = "";
}
}
}
}
public void print() {
System.out.print(list[0]);
for (int i = 0; i < n; i++)
System.out.print(" " + list[i]);
System.out.println(" ");
}
public void sort() {
for (int i = 0; i < n; i++) {
String key = list[i];
int j = i - 1;
while (j >= 0 && (list[j].compareToIgnoreCase(key) > 0))
{
list[j + 1] = list[j];
j = j - 1;
}
list[j + 1] = key;
}
}
}
That happens, when you hit end of the string and temp is not empty. To fix it you can add same if statement after loop:
for(int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
temp = temp + ch;
} else {
if(temp.length() > 0) {
list[n] = temp;
n++;
temp = "";
}
}
}
if(temp.length() > 0) {
list[n] = temp;
n++;
temp = "";
}
Also you would need to fix your output to not print first word twice:
public void print() {
for(int i = 0; i < n; i++) {
System.out.print(list[i] + " ");
}
System.out.println();
}
Output before fix:
a b c d e f g h i j k l m n o p q r s t
a a b c d e f g h i j k l m n o p q r s
Output after fix:
a b c d e f g h i j k l m n o p q r s t
a b c d e f g h i j k l m n o p q r s t
Update:
Also you can solve your problem in one line, using streams
public void read() {
Scanner in = new Scanner(System.in);
System.out.println("Please enter the sentence");
String s = in.nextLine();
String[] list = Arrays.stream(s.split(" ")).limit(20).sorted().toArray(String[]::new);
}
It splits input string by space character, takes first 20 words, sorts them and creates an array of them.
Output:
t s r q p o n m l k j i h g f e d c b a z z z z z
a b c d e f g h i j k l m n o p q r s t
You don't need to handle the end of the string explicitly: by using integer pointers to the start and end of words, you can do it as follows:
int start = 0;
while (start < s.length()) {
// Increment a start pointer until it points to the end of the string or the start of a word.
while (start < s.length() && !isLetter(s.charAt(start))) {
start++;
}
// Increment an end pointer until it points to the end of the string or a non-word character.
int end = start;
while (end < s.length() && isLetter(s.charAt(end))) {
end++;
}
if (start == end) {
// You reached the end of the string.
break;
}
// Grab the portion of the string between start and end, this is a word.
list[n++] = s.substring(start, end);
// Set up the start pointer to point to the end of this word, for the next iteration.
start = end;
}
where isLetter(char) is a method that checks if the argument is between A and Z (in either case).
I have seen a variation on this method which avoids the inner while loops: I don't like this as much, as I think it's less clear to read; but it doesn't have quite so much repetitive checking of the length (I think this code works, not tried it):
for (int start = 0, end = 0; start < s.length();) {
if (!isLetter(s.charAt(start))) {
start++;
end = start;
} else if (isLetter(s.charAt(end))) {
end++;
if (end >= s.length() || !isLetter(s.charAt(end))) {
list[n++] = s.substring(start, end);
start = end;
}
}
}
You have been given a binary string containing only the characters '1' and '0'.
Calculate how many characters of the string need to be changed in order to make the binary string such that each of its substrings of at least a certain length contains at least one "1" character.
I came to think of the following idea but it fails for many testcases:
public static int minimumMoves(String s, int d) {
int n = s.length();
int i=0, answer = 0;
while(i<n)
{
boolean hasOne = false;
int j=i;
while(j<n && j<i+d)
{
if(s.charAt(j) == '1')
{
hasOne = true;
break;
}
j++;
}
if(!hasOne) {
answer++;
i += d;
}
else i++;
}
return answer;
}
Also my algorithm runs on O(|s|2) time. Can anyone suggest ideas on O(|s|) time?
Just throwing off an idea:
return s.split("(?<=\\G.{" + String.valueof(d) + "})").stream().filter(str -> str.contains("1")).count()
You just need to break ensure there is no run of d zeros.
public static int minimumMoves(String s, int d) {
int result = 0;
int runLength = 0;
for(char c: s.toCharArray()) {
if (c == '0') {
runLength += 1;
if (runLength == d) { // we need to break this run
result += 1;
runLength = 0;
}
} else {
runLength = 0;
}
}
return result;
}
I used the sliding window technique and Deque to solve this. This is my accepted solution:
public static int minimumMoves(String s, int d) {
int n = s.length();
Deque<Character> dq = new LinkedList<>();
int count = 0, answer = 0;
for(int i=0; i<d; i++)
{
if(s.charAt(i) == '1') count++;
dq.addLast(s.charAt(i));
}
if(count == 0) {
answer++;
count++;
dq.removeLast();
dq.addLast('1');
}
int i=d;
while(i<n)
{
if(dq.getFirst() == '1') count--;
dq.removeFirst();
if(s.charAt(i) == '1') count++;
dq.addLast(s.charAt(i));
if(count == 0)
{
answer++;
dq.removeLast();
dq.addLast('1');
count++;
}
i++;
}
return answer;
}
You just need to use a sliding window and a count of 1s so far at each index. Use a sliding window of d and if you don't see any ones so far, update the last index of that window with 1 and increment the result.
Code below:
public static int minimumMoves(String s, int d) {
int n = s.length();
int[] count = new int[n+1];
int res = 0;
for ( int i = 1; i <= d; i++ ) {
if ( s.charAt(i-1) == '1') count[i] = count[i-1]+1;
else count[i] = count[i-1];
}
if ( count[d] == 0 ) {
res++;
count[d] = 1;
}
for ( int i = d+1; i <= n; i++ ) {
if ( s.charAt(i-1) == '0' ) {
count[i] = count[i-1];
int ones = count[i] - count[i-d];
if ( ones == 0 ) {
count[i] = count[i-1] + 1;
res++;
}
} else {
count[i] = count[i-1] + 1;
}
}
return res;
}
Thought of another implementation you can do for this by working from the maximum possible changes (assumes at start that all values are '0' in String), reduce it when it finds a '1' value, and then jump to the next substring start. This allows it to run in O(n) and Ω(n/m) (n = String length, m = Substring length).
public static int minimumMoves(String s, int d)
{
char[] a = s.toCharArray();
//Total possible changes (not counting tail)
if(a.length < d)
return 0;
int t = (int) a.length / d;
//Total possible changes (counting tail)
//int t = (int) Math.ceil((double) a.length / (double) d);
for(int i = 0; i < a.length; i++)
{
if(a[i] == '1')
{
t--;
//Calculate index for start of next substring
i = (i / d + 1) * d - 1;
}
}
return t;
}
Given a string representing the starting number and a maximum number of changes allowed, create the largest palindromic string of digits possible or the string -1 if it's impossible to create a palindrome under the contstraints.
I wrote a code who answer on the questions, but i have an error that i dont know where it is, or if even the code work.
static String highestValuePalindrome(String s, int n, int k) {
for(int i =0 ; i < n ; i++){
char[] ch =s.toCharArray();
if(n==1)
return s ;
else if ((ch[i] != ch[n-i-1]) && (k != 0) ){
ch[i] = ch[n-i-1] = 9 ;
k--;
}
}
String str = new String(ch);
return str ;
}
Output Format
Print a single line with the largest number that can be made by changing no more than digits. If this is not possible, print -1.
Sample Input
n=4, k=1
3943
Sample Output
3993
Sample Input
n=6, k=3
092282
Sample Output
992299
Sample Input
n=4, k=1
0011
Sample Output
-1
First of all there is no need to pass n as a parameter because it's just the length of the string. Secondly, this is not the complete program. I have made many changes to the given code.
public class largestPalindorme {
public static void main(String[] args) {
System.out.println(highestValuePalindrome("0011", 1));
}
static String highestValuePalindrome(String s, int k) {
char[] ch = s.toCharArray();
int n = s.length(); // which is same as n which you passed as parameter
int minChangesRequired = MinumumChangesNeeded(s);
//if the changes required to make the given string a palindrome is less then k then only it will go inside or it will return -1
if (k >= minChangesRequired) {
int diff = 0;
if (k > minChangesRequired) {
diff = k - minChangesRequired;
for (int l = 0; l < diff; l++) {
ch[l] = '9';
ch[n - l - 1] = '9';
}
}
for (int i = diff; i < n - diff / 2; i++) {
if (ch[i] != ch[n - i - 1]) {
//if checks which number is greater
int greater = Integer.parseInt(String.valueOf(ch[i])) > Integer.parseInt(String.valueOf(ch[n - i - 1])) ? Integer.parseInt(String.valueOf(ch[i])) : Integer.parseInt(String.valueOf(ch[n - i - 1]));
//replaces the smaller number from the greater number.
if (Integer.parseInt(String.valueOf(ch[i])) != greater) {
ch[i] = ch[n - i - 1];
} else {
ch[n - i - 1] = ch[i];
}
}
}
String str = new String(ch);
return str;
}
return "-1";
}
//this function returns the minimum changes we need to do to make it a palindrome.
public static int MinumumChangesNeeded(String s) {
int count = 0;
char[] ch = s.toCharArray();
int n = s.length();
for (int i = 0; i < n / 2; i++) {
if (ch[i] != ch[n - i - 1]) {
count++;
}
}
return count;}}
I have a String of a length of more than 1000 character. In this string i have to print 1st character after that every 5th character.
I tried writing a program to iterate from 0th character to last character and have a count variable.
If count is equal to 5. I am printing the character and count is initializing with 0.
private static String getMaskedToken(String token) {
if (token == null)
return null;
char[] charArray = token.toCharArray();
int length = token.length();
StringBuilder sb = new StringBuilder();
int count = 0;
for (int i = 0; i < length; i++) {
count++;
if (i == 0 || i == length - 1) {
sb.append(charArray[i]);
} else if (count == 5) {
sb.append(charArray[i]);
count=0;
} else if(count < 5 && i == length-1){
sb.append(charArray[i]);
}else {
sb.append('*');
}
}
return sb.toString();
}
Need to print last character if count is less than 5 of last
iteration.
If String of length 9, "12345678" then actual output will be like
1***5**8
If String of length 9, "123456789abcd" then actual output will be
like 1***5****a**d
String output = "";
for (int i = 0; i < str.length(); i++) {
if (i == 0) {
output += str.charAt(i);
output += "***";
output += str.charAt(4);
i = 4;
} else if ((i - 4) % 5 == 0) {
output += str.charAt(i);
} else if (i == str.length()-1) {
output += str.charAt(i);
} else {
output += "*";
}
}
System.out.println(output);
}
This will print 1***5****a**d for string "123456789abcd".
try this code:
public void printFirstAndEveryFifthCharecter(String str)
{
for (int i = 0 ; i < str.length() ; i++)
{
if ((i+1) == 1 | (i+1) % 5 == 0) {
System.out.print(str.charAt(i) + "***");
}
}
if (str.length() % 5 > 0) {
System.out.print(str.charAt(str.length() - 1));
}
}
Your code should work fine. Here's an alternative without using StringBuilder and with fewer checks.
private static String getFirstFifthLast(String str) {
String[] strArray = str.split(""); //returns an array of strings with length 1
int arrayLength = strArray.length;
String result = strArray[0]; //append the first element
//append element if it is in 5th position, append "*" otherwise
for (int i = 0; i < arrayLength; i++) {
if ((i + 1) % 5 == 0) {
result += strArray[i];
} else {
result += "*";
}
}
result += strArray[arrayLength - 1]; //append the last element
return result;
}
Try this code,
private void printEvery5thCharacter(String str) {
for (int i = 1; i < str.length() - 1; i += 5) {
System.out.print(str.charAt(i - 1) + "***");
if (i == 1) {
i = 0;
}
}
if (str.length() % 5 > 0) {
System.out.print(str.charAt(str.length() - 1));
}
}
Take as input S, a string. Write a function that replaces every odd character with the character having just higher ASCII code and every even character with the character having just lower ASCII code. Print the value returned.
package assignments;
import java.util.Scanner;
public class strings_odd_even_char {
static Scanner scn = new Scanner(System.in);
public static void main(String[] args) {
String str = scn.nextLine();
for (int i = 0; i < str.length(); i = i + 2) {
char ch = str.charAt(i);
ch = (char)((ch + 1));
System.out.println(ch);
}
for (int j = 1; j < str.length(); j = j + 2) {
char ch = str.charAt(j);
ch = (char)((ch - 1));
System.out.print(ch);
}
}
}
The problem with my code is that it is first printing the values for all the odd characters and then for even characters but what I want is that they get printed in proper sequence like for input --> abcg , the output should be --> badf .
I'd hold the "incremenet" value in a variable and alternate it between +1 and -1 as I go voer the characters:
private static String change(String s) {
StringBuilder sb = new StringBuilder(s.length());
int increment = 1;
for (int i = 0; i < s.length(); ++i) {
sb.append((char)(s.charAt(i) + increment));
increment *= -1;
}
return sb.toString();
}
Just use one loop that handles both characters:
for (int i = 0; i < str.length(); i = i + 2) {
char ch = str.charAt(i);
ch = (char) (ch + 1);
System.out.print(ch);
if (i + 1 < str.length()) {
ch = str.charAt(i + 1);
ch = (char) (ch - 1);
System.out.print(ch);
}
}
You only need to iterate one time but do different operation (char+1) or (char-1) depending on the i:
public static void main(String[] args) {
String str = scn.nextLine();
for(int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if(i % 2 == 0) { // even
ch += 1;
} else { // odd
ch -= 1;
}
System.out.print(ch);
}
}
You are using two loops, but you only need one. You can use the % operator to tell if i is even or odd, and then either subtract or add accordingly:
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if(i % 2 == 0) {
ch = (char)((ch + 1));
System.out.println(ch);
} else {
ch = (char)((ch - 1));
System.out.print(ch);
}
}
You can do it in one for loop, to do that you will need to check whether the current index is even or odd. if current index is even you will increment char and print, if it is odd you will decrement char and print. to check if even or odd using % operator
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if(i%2 == 0) {
ch = ch + 1;
System.out.println(ch);
continue;
}
ch = ch - 1;
System.out.println(ch);
}