Review an answer - Decode Ways - java

I'm trying to solve a question and my question here is why doesn't my solution work?. Here's the question and below's the answer.
Question taken from leetcode: http://oj.leetcode.com/problems/decode-ways/
A message containing letters from A-Z is being encoded to numbers using the following mapping:
'A' -> 1
'B' -> 2
...
'Z' -> 26
Given an encoded message containing digits, determine the total number of ways to decode it.
For example,Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12). The number of ways decoding "12" is 2.
My solution:
The point with my solution is going backwards and multiplying the number of options if a split is found. By split I mean that digits can be interpreted in two ways. For example: 11 can interpreted in two ways 'aa' or 'k'.
public class Solution {
public int numDecodings(String s) {
if (s.isEmpty() || s.charAt(0) == '0') return 0;
int decodings = 1;
boolean used = false; // Signifies that the prev was already use as a decimal
for (int index = s.length()-1 ; index > 0 ; index--) {
char curr = s.charAt(index);
char prev = s.charAt(index-1);
if (curr == '0') {
if (prev != '1' && prev != '2') {
return 0;
}
index--; // Skip prev because it is part of curr
used = false;
} else {
if (prev == '1' || (prev == '2' && curr <= '6')) {
decodings = decodings * 2;
if (used) {
decodings = decodings - 1;
}
used = true;
} else {
used = false;
}
}
}
return decodings;
}
}
The failure is on the following input:
Input:"4757562545844617494555774581341211511296816786586787755257741178599337186486723247528324612117156948"
Output: 3274568
Expected: 589824

This is a really interesting problem. First, I will show how I would solve this problem. We will see that it is not that complicated when using recursion, and that the problem can be solved using dynamic programming. We will produce a general solution that does not hardcode an upper limit of 26 for each code point.
A note on terminology: I will use the term code point (CP) not in the Unicode sense, but to refer to one of the code numbers 1 though 26. Each code point is represented as a variable number of characters. I will also use the terms encoded text (ET) and clear text (CT) in their obvious meanings. When talking about a sequence or array, the first element is called the head. The remaining elements are the tail.
Theoretical Prelude
The EC "" has one decoding: the CT "".
The EC "3" can be destructured into '3' + "", and has one decoding.
The EC "23" can be destructured as '2' + "3" or '23' + "". Each of the tails has one decoding, so the whole EC has two decodings.
The EC "123" can be destructured as '1' + "23" or '12' + "3". The tails have two and one decodings respectively. The whole EC has three decodings. The destructuring '123' + "" is not valid, because 123 > 26, our upper limit.
… and so on for ECs of any length.
So given a string like "123", we can obtain the number of decodings by finding all valid CPs at the beginning, and summing up the number of decodings of each tail.
The most difficult part of this is to find valid heads. We can get the maximal length of the head by looking at a string representation of the upper limit. In our case, the head can be up to two characters long. But not all heads of appropriate lengths are valid, because they have to be ≤ 26 as well.
Naive Recursive Implementation
Now we have done all the necessary work for a simple (but working) recursive implementation:
static final int upperLimit = 26;
static final int maxHeadSize = ("" + upperLimit).length();
static int numDecodings(String encodedText) {
// check base case for the recursion
if (encodedText.length() == 0) {
return 1;
}
// sum all tails
int sum = 0;
for (int headSize = 1; headSize <= maxHeadSize && headSize <= encodedText.length(); headSize++) {
String head = encodedText.substring(0, headSize);
String tail = encodedText.substring(headSize);
if (Integer.parseInt(head) > upperLimit) {
break;
}
sum += numDecodings(tail);
}
return sum;
}
Cached Recursive Implementation
Obviously this isn't very efficient, because (for longer ETs), the same tail will be analyzed multiple times. Also, we create a lot of temporary strings, but we'll let that be for now. One thing we can easily do is to memoize the number of decodings of a specific tail. For that, we use an array of the same length as the input string:
static final int upperLimit = 26;
static final int maxHeadSize = ("" + upperLimit).length();
static int numDecodings(String encodedText) {
return numDecodings(encodedText, new Integer[1 + encodedText.length()]);
}
static int numDecodings(String encodedText, Integer[] cache) {
// check base case for the recursion
if (encodedText.length() == 0) {
return 1;
}
// check if this tail is already known in the cache
if (cache[encodedText.length()] != null) {
return cache[encodedText.length()];
}
// cache miss -- sum all tails
int sum = 0;
for (int headSize = 1; headSize <= maxHeadSize && headSize <= encodedText.length(); headSize++) {
String head = encodedText.substring(0, headSize);
String tail = encodedText.substring(headSize);
if (Integer.parseInt(head) > upperLimit) {
break;
}
sum += numDecodings(tail, cache); // pass the cache through
}
// update the cache
cache[encodedText.length()] = sum;
return sum;
}
Note that we use an Integer[], not an int[]. This way, we can check for non-existent entries using a test for null. This solution is not only correct, it is also comfortably fast – naive recursion runs in O(number of decodings) time, while the memoized version runs in O(string length) time.
Towards a DP Solution
When you run above code in your head, you will notice that the first invocation with the whole string will have a cache miss, then calculate the number of decodings for the first tail, which also misses the cache every time. We can avoid this by evaluating the tails first, starting from the end of the input. Because all tails will have been evaluated before the whole string is, we can remove the checks for cache misses. Now we also don't have any reason for recursion, because all previous results are already in the cache.
static final int upperLimit = 26;
static final int maxHeadSize = ("" + upperLimit).length();
static int numDecodings(String encodedText) {
int[] cache = new int[encodedText.length() + 1];
// base case: the empty string at encodedText.length() is 1:
cache[encodedText.length()] = 1;
for (int position = encodedText.length() - 1; position >= 0; position--) {
// sum directly into the cache
for (int headSize = 1; headSize <= maxHeadSize && headSize + position <= encodedText.length(); headSize++) {
String head = encodedText.substring(position, position + headSize);
if (Integer.parseInt(head) > upperLimit) {
break;
}
cache[position] += cache[position + headSize];
}
}
return cache[0];
}
This algorithm could be optimized further by noticing that we only ever query the last maxHeadSize elements in the cache. So instead of an array, we could use a fixed-sized queue. At that point, we would have a dynamic programming solution that runs in *O(input length) time and O(maxHeadSize) space.
Specialization for upperLimit = 26
The above algorithms were kept as general as possible, but we can go and manually specialize it for a specific upperLimit. This can be useful because it allows us to do various optimizations. However, this introduces “magic numbers” that make the code harder to maintain. Such manual specializations should therefore be avoided in non-critical software (and the above algorithm is already as fast as it gets).
static int numDecodings(String encodedText) {
// initialize the cache
int[] cache = {1, 0, 0};
for (int position = encodedText.length() - 1; position >= 0; position--) {
// rotate the cache
cache[2] = cache[1];
cache[1] = cache[0];
cache[0] = 0;
// headSize == 1
if (position + 0 < encodedText.length()) {
char c = encodedText.charAt(position + 0);
// 1 .. 9
if ('1' <= c && c <= '9') {
cache[0] += cache[1];
}
}
// headSize == 2
if (position + 1 < encodedText.length()) {
char c1 = encodedText.charAt(position + 0);
char c2 = encodedText.charAt(position + 1);
// 10 .. 19
if ('1' == c1) {
cache[0] += cache[2];
}
// 20 .. 26
else if ('2' == c1 && '0' <= c2 && c2 <= '6') {
cache[0] += cache[2];
}
}
}
return cache[0];
}
Comparision with your code
The code is superficially similar. However, your parsing around characters is more convoluted. You have introduced a used variable that, if set, will decrement the decode count in order to account for double-character CPs. This is wrong, but I am not sure why. The main problem is that you are doubling the count at almost every step. As we have seen, the previous counts are added, and may very well be different.
This indicates that you wrote the code without proper preparation. You can write many kinds of software without having to think too much, but you can't do without careful analysis when designing an algorithm. For me, it is often helpful to design an algorithm on paper, and draw diagrams of each step (along the lines of the “Theoretical Prelude” of this answer). This is especially useful when you are thinking too much about the language you are going to implement in, and too little about possibly wrong assumptions.
I suggest that you read up on “proofs by induction” to understand how to write a correct recursive algorithm. Once you have a recursive solution, you can always translate it into an iterative version.

So here is some what simpler way out for your problem. This is pretty close to calculating Fibonacci, with the difference that there are condition checks on each smaller size subproblem.
The space complexity is O(1) and time is O(n)
The code is in C++.
int numDecodings(string s)
{
if( s.length() == 0 ) return 0;
int j = 0;
int p1 = (s[j] != '0' ? 1 : 0); // one step prev form j=1
int p2 = 1; // two step prev from j=1, empty
int p = p1;
for( int j = 1; j < s.length(); j++ )
{
p = 0;
if( s[j] != '0' )
p += p1;
if( isValidTwo(s, j-1, j) )
p += p2;
if( p==0 ) // no further decoding necessary,
break; // as the prefix 0--j is has no possible decoding.
p2 = p1; // update prev for next j+1;
p1 = p;
}
return p;
}
bool isValidTwo(string &s, int i, int j)
{
int val= 10*(s[i]-'0')+s[j]-'0';
if ( val <= 9 )
return false;
if ( val > 26 )
return false;
return true;
}

Here is my code to solve the problem. I use DP , I think it's clear to understand.
Written in Java
public class Solution {
public int numDecodings(String s) {
if(s == null || s.length() == 0){
return 0;
}
int n = s.length();
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = s.charAt(0) != '0' ? 1 : 0;
for(int i = 2; i <= n; i++){
int first = Integer.valueOf(s.substring(i-1,i));
int second = Integer.valueOf(s.substring(i-2,i));
if(first >= 1 && first <= 9){
dp[i] += dp[i-1];
}
if(second >= 10 && second <= 26){
dp[i] += dp[i-2];
}
}
return dp[n];
}
}

Since I struggled with this problem myself, here is my solution and reasoning. Probably I will mostly repeat what amon wrote, but maybe someone will find it helpful. Also it's c# not java.
Let's say that we have input "12131" and want to obtain all possible decoded strings.
Straightforward recursive solution would do iterate from left to right, obtain valid 1 and 2 digits heads, and invoke function recursively for tail.
We can visualize it using a tree:
There are 5 leaves and this is number of all possible decoded strings. There are also 3 empty leaves, because number 31 cannot be decoded into letter, so these leaves are invalid.
Algorithm might look like this:
public IList<string> Decode(string s)
{
var result = new List<string>();
if (s.Length <= 2)
{
if (s.Length == 1)
{
if (s[0] != '0')
result.Add(this.ToASCII(s));
}
else if (s.Length == 2)
{
if (s[0] != '0' && s[1] != '0')
result.Add(this.ToASCII(s.Substring(0, 1)) + this.ToASCII(s.Substring(1, 1)));
if (s[0] != '0' && int.Parse(s) > 0 && int.Parse(s) <= 26)
result.Add(this.ToASCII(s));
}
}
else
{
for (int i = 1; i <= 2; ++i)
{
string head = s.Substring(0, i);
if (head[0] != '0' && int.Parse(head) > 0 && int.Parse(head) <= 26)
{
var tails = this.Decode(s.Substring(i));
foreach (var tail in tails)
result.Add(this.ToASCII(head) + tail);
}
}
}
return result;
}
public string ToASCII(string str)
{
int number = int.Parse(str);
int asciiChar = number + 65 - 1; // A in ASCII = 65
return ((char)asciiChar).ToString();
}
We have to take care of numbers starting with 0 ("0", "03", etc.), and greater than 26.
Because in this problem we need only count decoding ways, and not actual strings, we can simplify this code:
public int DecodeCount(string s)
{
int count = 0;
if (s.Length <= 2)
{
if (s.Length == 1)
{
if (s[0] != '0')
count++;
}
else if (s.Length == 2)
{
if (s[0] != '0' && s[1] != '0')
count++;
if (s[0] != '0' && int.Parse(s) > 0 && int.Parse(s) <= 26)
count++;
}
}
else
{
for (int i = 1; i <= 2; ++i)
{
string head = s.Substring(0, i);
if (head[0] != '0' && int.Parse(head) > 0 && int.Parse(head) <= 26)
count += this.DecodeCount(s.Substring(i));
}
}
return count;
}
The problem with this algorithm is that we compute results for the same input string multiple times. For example there are 3 nodes ending with 31: ABA31, AU31, LA31. Also there are 2 nodes ending with 131: AB131, L131.
We know that if node ends with 31 it has only one child, since 31 can be decoded only in one way to CA. Likewise, we know that if string ends with 131 it has 2 children, because 131 can be decoded into ACA or LA. Thus, instead of computing it all over again we can cache it in map, where key is string (eg: "131"), and value is number of decoded ways:
public int DecodeCountCached(string s, Dictionary<string, int> cache)
{
if (cache.ContainsKey(s))
return cache[s];
int count = 0;
if (s.Length <= 2)
{
if (s.Length == 1)
{
if (s[0] != '0')
count++;
}
else if (s.Length == 2)
{
if (s[0] != '0' && s[1] != '0')
count++;
if (s[0] != '0' && int.Parse(s) > 0 && int.Parse(s) <= 26)
count++;
}
}
else
{
for (int i = 1; i <= 2; ++i)
{
string head = s.Substring(0, i);
if (head[0] != '0' && int.Parse(head) > 0 && int.Parse(head) <= 26)
count += this.DecodeCountCached(s.Substring(i), cache);
}
}
cache[s] = count;
return count;
}
We can refine this even further. Instead of using strings as a keys, we can use length, because what is cached is always tail of input string. So instead of caching strings: "1", "31", "131", "2131", "12131" we can cache lengths of tails: 1, 2, 3, 4, 5:
public int DecodeCountDPTopDown(string s, Dictionary<int, int> cache)
{
if (cache.ContainsKey(s.Length))
return cache[s.Length];
int count = 0;
if (s.Length <= 2)
{
if (s.Length == 1)
{
if (s[0] != '0')
count++;
}
else if (s.Length == 2)
{
if (s[0] != '0' && s[1] != '0')
count++;
if (s[0] != '0' && int.Parse(s) > 0 && int.Parse(s) <= 26)
count++;
}
}
else
{
for (int i = 1; i <= 2; ++i)
{
string head = s.Substring(0, i);
if (s[0] != '0' && int.Parse(head) > 0 && int.Parse(head) <= 26)
count += this.DecodeCountDPTopDown(s.Substring(i), cache);
}
}
cache[s.Length] = count;
return count;
}
This is recursive top-down dynamic programming approach. We start from the begining, and then recursively compute solutions for tails, and memoize those results for further use.
We can translate it to bottom-up iterative DP solution. We start from the end and cache results for tiles like in previous solution. Instead of map we can use array because keys are integers:
public int DecodeCountBottomUp(string s)
{
int[] chache = new int[s.Length + 1];
chache[0] = 0; // for empty string;
for (int i = 1; i <= s.Length; ++i)
{
string tail = s.Substring(s.Length - i, i);
if (tail.Length == 1)
{
if (tail[0] != '0')
chache[i]++;
}
else if (tail.Length == 2)
{
if (tail[0] != '0' && tail[1] != '0')
chache[i]++;
if (tail[0] != '0' && int.Parse(tail) > 0 && int.Parse(tail) <= 26)
chache[i]++;
}
else
{
if (tail[0] != '0')
chache[i] += chache[i - 1];
if (tail[0] != '0' && int.Parse(tail.Substring(0, 2)) > 0 && int.Parse(tail.Substring(0, 2)) <= 26)
chache[i] += chache[i - 2];
}
}
return chache.Last();
}
Some people simplify it even further, initializing cache[0] with value 1, so they can get rid of conditions for tail.Length==1 and tail.Length==2. For me it is unintuitive trick though, since clearly for empty string there is 0 decode ways not 1, so in such case additional condition must be added to handle empty input:
public int DecodeCountBottomUp2(string s)
{
if (s.Length == 0)
return 0;
int[] chache = new int[s.Length + 1];
chache[0] = 1;
chache[1] = s.Last() != '0' ? 1 : 0;
for (int i = 2; i <= s.Length; ++i)
{
string tail = s.Substring(s.Length - i, i);
if (tail[0] != '0')
chache[i] += chache[i - 1];
if (tail[0] != '0' && int.Parse(tail.Substring(0, 2)) > 0 && int.Parse(tail.Substring(0, 2)) <= 26)
chache[i] += chache[i - 2];
}
return chache.Last();
}

My solution is based on the idea that the arrangement of items(char/digit) within a particular substring is completely independent of the same within a different substring.
So we need to multiply each of those independent ways to get the total number of ways.
// nc is the number of consecutive 1's or 2's in a substring.
// Returns the number of ways these can be arranged within
// themselves to a valid expr.
int ways(int nc){
int n = pow(2, (nc/2)); //this part can be memorized using map for optimization
int m = n;
if (nc%2) {
m *= 2;
}
return n + m - 1;
}
bool validTens(string A, int i){
return (A[i] == '1' || (A[i] == '2' && A[i+1] <= '6'));
}
int numDecodings(string A) {
int ans = 1;
int nc;
if ((A.length() == 0)||(A[0] == '0')) return 0;
for(int i = 1; i < A.length();i++){
if(A[i] == '0' && validTens(A, i-1) == false) return 0; //invalid string
while(i < A.length() && validTens(A, i-1)) {
if(A[i] == '0'){
//think of '110' or '1210', the last two digits must be together
if(nc > 0) nc--;
}
else nc++;
i++;
}
ans *= ways(nc);
nc = 0;
}
return ans;
}

Java solution with space and time complexity O(n)
public int numDecodings(String s) {
int n = s.length();
if (n > 0 && s.charAt(0) == '0')
return 0;
int[] d = new int[n + 1];
d[0] = 1;
d[1] = s.charAt(0) != '0' ? 1 : 0;
for (int i = 2; i <= n; i++) {
if (s.charAt(i - 1) > '0')
d[i] = d[i] + d[i - 1];
if (s.charAt(i - 2) == '2' && s.charAt(i - 1) < '7')
d[i] = d[i - 2] + d[i];
if (s.charAt(i - 2) == '1' && s.charAt(i - 1) <= '9')
d[i] = d[i - 2] + d[i];
}
return d[n];
}

Here is an O(N) C++ DP implementation.
int numDecodings(string s) {
if(s[0] == '0') return 0; // Invalid Input
int n = s.length();
// dp[i] denotes the number of ways to decode the string of length 0 to i
vector<int> dp(n+1, 0);
// base case : string of 0 or 1 characters will have only 1 way to decode
dp[0] = dp[1] = 1;
for(int i = 2; i <= n; i++) {
// considering the previous number
if(s[i-1] > '0') dp[i] += dp[i-1];
// considering the previous two numbers
if(s[i-2] == '1' || (s[i-2] == '2' && s[i-1] < '7')) dp[i] += dp[i-2];
}
return dp[n];
}

Related

Time complexity of longest palindrome finding algorithm

Can some one help me out on this algorithm time complexity? It`s a leetcode task to find the longest palindromic substring (https://leetcode.com/problems/longest-palindromic-substring/). I belive this is O(n³), but how to count this? My approach was: for loop goes s.length times (call it n), inner loop, in a worse case where string consist of equal chars, will expand the window n times, hence O(n²). But working time seems to be to big for O(n^2).
Here's code:
public String longestPalindrome(String s) {
if (s.length() == 1) {
return s;
}
String max = "";
for (int i = 0; i < s.length() - 1; i++) {
int lpointer = i;
int rpointer = i + 1;
while (rpointer < i + 3) {
int t = rpointer;
System.out.println(lpointer + " " + rpointer);
while (lpointer >= 0 && rpointer < s.length() && s.charAt(lpointer) == s.charAt(rpointer)) {
lpointer--;
rpointer++;
}
String temp = s.substring(lpointer + 1, rpointer);
max = max.length() < temp.length() ? temp : max;
rpointer = t + 1;
lpointer = i;
if (rpointer == s.length()) {
break;
}
}
}
return max == "" ? String.valueOf(s.charAt(0)) : max;
}
Much appreciate any help!
I’d say your algorithm is an O(n²) even though you have three loops:
The time-complexity of the for loop is quite easy: You have n iterations for a n long string. The inner while loops are a bit more complex:
I noticed that outside the inner while loop the both cursers have always the difference from 1 or 2. I guess that’s because you want to check for even and uneven palindromes. That means that the inner while loop is only running twice for every iteration of the for loop. In addition, the inner while loop can max run the half the length of the string because it searches in both directions. This leads to both while loops having a complexity of 2 * n/2 which equals n. So n*n leads to O(n²).
Still, besides the time-complexity, your code runs very slow (>700ms) compared to the average submissions.
With some optimization I was able to reproduce an O(n²) algorithm with only 15ms running time. It uses pretty much your attempt. The only major difference is that instead of having the two inner while loops to differentiate between even and uneven numbers it uses two different separate loops:
public static String longestPalindrome(String s) {
if (s.length() <= 0) return "";
char[] chars = s.toCharArray();
int longest = 0;
int iLongest = 0;
for (int i = 0; i < chars.length; i++) { //position to start from
if (longest == chars.length || (chars.length - 1 == longest && ((chars.length % 2 != 0 && longest % 2 == 0) || i - 1 > longest / 2))) break; //skip useless iterations
//uneven
for (int j = 0; j <= (chars.length - 1 - i < i ? chars.length - 1 - i : i); j++) { //max length possible in both directions
if (chars[i+j] == chars[i-j]) {
int nLongest = j*2+1;
if (nLongest > longest) {
longest = nLongest;
iLongest = i;
}
} else break;
}
//even
for (int j = 0; j <= (chars.length - 1 - i < i ? chars.length - 1 - i : i - 1); j++) {
if (chars[i+j] == chars[i-j-1]) {
int nLongest = (j+1)*2;
if (nLongest > longest) {
longest = nLongest;
iLongest = i;
}
} else break;
}
}
int start = iLongest - ((longest % 2 == 0 ? longest : longest - 1) / 2);
int stop = iLongest + ((longest - 1) / 2);
return s.substring(start, stop + 1);
}
Hope that answeres your question.

Rat in a Maze using Stack (Java)

We are given a 2D character array and, starting from a given point, we have to find an 'exit', which is a '0' in the perimeter of the given matrix. The program returns true if a path is found, which will print a message when I run it in my main method later. We can only move up, left, down or right to a nearby '0'. I have already tried to make this work, however I get a memory error, which probably means I am stuck in an infinite loop. I have tried to implement this project using a Stack that I made using Nodes, which I also implemented myself using generics, like this: e.g.
StringStackImpl<int[][]> s = new StringStackImpl<>();
in a different .java file. These work fine. I tried to store the coordinates of a character in the matrix like this:
StringStackImpl<int[]> s = new StringStackImpl<>();
s.push(new int[]{i, j});
Here is my code:
private static boolean hasExit(char[][] maze, int n, int m, int i, int j) {
int d = -1;
boolean[][] visited = new boolean[n][m];
for (int a = 0; a < visited.length; a++) {
for (int b = 0; b < (visited[a]).length; b++) {
visited[a][b] = false;
}
}
StringStackImpl<int[]> s = new StringStackImpl<>();
s.push(new int[]{i, j});
while (!(s.isEmpty())) {
int[] temp = s.peek();
d += 1;
i = temp[0];
j = temp[1];
if (((i == 0) || (i == n-1) || (j == 0) || (j == m-1)) && (maze[i][j] == '0')) {
return true;
}
if (d == 0) {
if ((i-1 >= 0) && (maze[i-1][j] == '0') && !(visited[i-1][j])) {
visited[i-1][j] = true;
s.push(new int[]{i-1, j});
d = -1;
}
}
else if (d == 1) {
if ((j-1 >= 0) && (maze[i][j-1] == '0') && !(visited[i][j-1])) {
visited[i][j-1] = true;
s.push(new int[]{i, j-1});
d = -1;
}
}
else if (d == 2) {
if ((i+1 < n) && (maze[i+1][j] == '0') && !(visited[i+1][j])) {
visited[i+1][j] = true;
s.push(new int[]{i+1, j});
d = -1;
}
}
else if (d == 3) {
if ((j+1 < m) && (maze[i][j+1] == '0') && !(visited[i][j+1])) {
visited[i][j+1] = true;
s.push(new int[]{i, j+1});
d = -1;
}
}
else {
s.pop();
d = -1;
}
}
return false;
}
}
EDIT: It works now, thanks to kendavidson! <3
At first glance, and trusting you about StringStackImpl, I think you should only use d = -1;in the last else. Right now you are always setting it back to -1, and so you only check the d == 0 condition. From what I understood, d is the direction possible, and so should go from 0 to 3 ?
Other than that, take some time to give meaningfull names to your variables :
visited[i][j] is true when the node [i][j] has not been visited yet... That's counter-intuitive, and doesn't help others, or yourself, from understanding your code.
Are you forced to use char for i and j ? It really tends to make the code long and tedious for nothing.

what can cause this particular palindrome a logic error

The following method was written to determine whether its String parameter reads identically left-to-right and right-to-left (the so called palindrome). I am having trouble finding the logic error of this palindrome. I believe the error is that the two conditions in the whole loop can affect checking the characters in the string. Please correct me if I am wrong so I can propose a proper solution.
This may be a stupid question to many of you, but I am new to java programming and this is written question on paper not actual code if that makes sense.
Your logic will only work if the length of input string is an odd number, i.e. 1,3,5 etc.
Because in case the length is even, i will never be equal to j. Example for a string "abba":
while (i == j && S.charAt(i) == S.charAt(j)) { // i = 0, j = 3
i++;
j--;
}
iteration-2:
while (i <= j && S.charAt(i) == S.charAt(j)) { // i = 1 , j = 2
i++;
j--;
}
iteration-3:
while (i <= j && S.charAt(i) == S.charAt(j)) { // i = 2 , j = 1
i++;
j--;
}
This will finally result in StringIndexOutOfBoundsException when i reaches negative value and j reaches a value greater than length of string.
Try below code:
static boolean isPalidrome(String s) {
int i = 0;
int j = s.length() - 1;
while( i <= j && s.charAt(i) == s.charAt(j)) {
i ++;
j--;
}
return i >= j;
}
your code will fail if input string is something like "zz" or "xxxx" meaning even length with same characters so ideally you can try with something like this :
public static boolean isPal(String str) {
int start = 0;
int end = str.length() - 1;
while (start < end) {
final char f = str.charAt(start++);
final char b = str.charAt(end--);
if (f != b) {
return false;
}
}
return true;
}
You may apply following changes in your own code. There are basically two changes which are in your termination condition of while loop i.e. i <= j and return condition of (i >= j)
public static boolean isPalindrome(String S) {
int i = 0, j = S.length() - 1;
while (i <= j && S.charAt(i) == S.charAt(j)) {
i++;
j--;
}
return (i >= j);
}

minimum operations required to make the longest character interval equal to K

I was asked this question in an contest.
Given a string containing only M and L, we can change any "M" to "L" or any "L" to "M". The objective of this function is to calculate the minimum number of changes we have to make in order to achieve the desired longest M-interval length K.
For example, given S = "MLMMLLM" and K = 3, the function should return 1. We can change the letter at position 4 (counting from 0) to obtain "MLMMMLM", in which the longest interval of letters "M" is exactly three characters long.
For another example, given S = "MLMMMLMMMM" and K = 2, the function should return 2. We can, for example, modify the letters at positions 2 and 7 to get the string "MLLMMLMLMM", which satisfies the desired property.
Here's what I have tried till now, but I am not getting correct output:
I am traversing the string and whenever longest char count exceeds K, I'm replacing M with L that point.
public static int solution(String S, int K) {
StringBuilder Str = new StringBuilder(S);
int longest=0;int minCount=0;
for(int i=0;i<Str.length();i++){
char curr=S.charAt(i);
if(curr=='M'){
longest=longest+1;
if(longest>K){
Str.setCharAt(i, 'L');
minCount=minCount+1;
}
}
if(curr=='L')
longest=0;
}
if(longest < K){
longest=0;int indexoflongest=0;minCount=0;
for(int i=0;i<Str.length();i++){
char curr=S.charAt(i);
if(curr=='M'){
longest=longest+1;
indexoflongest=i;
}
if(curr=='L')
longest=0;
}
Str.setCharAt(indexoflongest, 'M');
minCount=minCount+1;
}
return minCount;
}
There are 2 parts to this algorithm as we want to get the longest character interval equal to K.
We already have a interval >= K so now we need to appropriately change some characters so we greedily change every (k + 1) th character and again start counting from 0.
Now if the interval was less than K I will need to run a sliding window over the array. While running this window I am basically considering converting all L's to M's in this window of length K. But this comes with a side effect of increasing the length of the interval as there could be K's outside so this variable (int nec) keeps track of that. So now I have to also consider converting the 2 possible M's outside the (K length) window to L's.
Here's the complete runnable code in C++. Have a good day.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector <int> vi;
typedef pair<int, int> ii;
int change(string s, int k) {
// handling interval >= k
bool flag = false;
int ans = 0;
int cnt = 0;
for(int i=0; i<s.size(); i++) {
if(s[i] == 'M') cnt++;
else cnt = 0;
if(cnt == k) flag = true;
if(cnt > k) s[i] = 'L', ans++, cnt = 0;
}
if(flag) return ans;
// handling max interval < k
// If the interval is too big.
if(k > s.size()) {
cerr << "Can't do it.\n"; exit(0);
}
// Sliding window
cnt = 0;
for(int i=0; i<k; i++) {
if(s[i] == 'L') cnt++;
}
ans = cnt + (s[k] == 'M'); // new edit
int nec = 0; // new edit
for(int i=k; i<s.size(); i++) {
if(s[i-k] == 'L') cnt--;
if(s[i] == 'L') cnt++;
nec = 0;
if(i-k != 0 && s[i-k-1] == 'M')
nec++;
if(i < s.size()-1 && s[i+1] == 'M')
nec++;
ans = min(ans, cnt + nec);
}
return ans;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
string s;
int k;
cin >> s >> k;
int ans = change(s, k);
cout << ans << "\n";
return 0;
}
int
process_data(const char *m, int k)
{
int m_cnt = 0, c_cnt = 0;
char ch;
const char *st = m;
int inc_cnt = -1;
int dec_cnt = -1;
while((ch = *m++) != 0) {
if (m_cnt++ < k) {
c_cnt += ch == 'M' ? 0 : 1;
if ((m_cnt == k) && (
(inc_cnt == -1) || (inc_cnt > c_cnt))) {
inc_cnt = c_cnt;
}
}
else if (ch == 'M') {
if (*st++ == 'M') {
/*
* losing & gaining M carries no change provided
* there is atleast one L in the chunk. (c_cnt != 0)
* Else it implies stretch of Ms
*/
if (c_cnt <= 0) {
int t;
c_cnt--;
/*
* compute min inserts needed to brak the
* stretch to meet max of k.
*/
t = (k - c_cnt) / (k+1);
dec_cnt += t;
}
}
else {
ASSERT(c_cnt > 0, "expect c_cnt(%d) > 0", c_cnt);
ASSERT(inc_cnt != -1, "expect inc_cnt(%d) != -1", inc_cnt);
/* Losing L and gaining M */
if (--c_cnt < inc_cnt) {
inc_cnt = c_cnt;
}
}
}
else {
if (c_cnt <= 0) {
/*
* take this as a first break and restart
* as any further addition of M should not
* happen. Ignore this L
*/
st = m;
c_cnt = 0;
m_cnt = 0;
}
else if (*st++ == 'M') {
/* losing m & gaining l */
c_cnt++;
}
else {
// losing & gaining L; no change
}
}
}
return dec_cnt != -1 ? dec_cnt : inc_cnt;
}
Corrected code:
int
process_data(const char *m, int k)
{
int m_cnt = 0, c_cnt = 0;
char ch;
const char *st = m;
int inc_cnt = -1;
int dec_cnt = -1;
while((ch = *m++) != 0) {
if (m_cnt++ < k) {
c_cnt += ch == 'M' ? 0 : 1;
if ((m_cnt == k) && (
(inc_cnt == -1) || (inc_cnt > c_cnt))) {
inc_cnt = c_cnt;
}
}
else if (ch == 'M') {
if (*st++ == 'M') {
/*
* losing & gaining M carries no change provided
* there is atleast one L in the chunk. (c_cnt != 0)
* Else it implies stretch of Ms
*/
if (c_cnt <= 0) {
c_cnt--;
}
}
else {
ASSERT(c_cnt > 0, "expect c_cnt(%d) > 0", c_cnt);
ASSERT(inc_cnt != -1, "expect inc_cnt(%d) != -1", inc_cnt);
/* Losing L and gaining M */
if (--c_cnt < inc_cnt) {
inc_cnt = c_cnt;
}
}
}
else {
if (c_cnt <= 0) {
/*
* compute min inserts needed to brak the
* stretch to meet max of k.
*/
dec_cnt += (dec_cnt == -1 ? 1 : 0) + ((k - c_cnt) / (k+1));
/*
* take this as a first break and restart
* as any further addition of M should not
* happen. Ignore this L
*/
st = m;
c_cnt = 0;
m_cnt = 0;
}
else if (*st++ == 'M') {
/* losing m & gaining l */
c_cnt++;
}
else {
// losing & gaining L; no change
}
}
}
if (c_cnt <= 0) {
/*
* compute min inserts needed to brak the
* stretch to meet max of k.
*/
dec_cnt += (dec_cnt == -1 ? 1 : 0) + ((k - c_cnt) / (k+1));
}
return dec_cnt != -1 ? dec_cnt : inc_cnt;
}

Square pattern java

Create void method that will put on screen square with patern like that :
xoxo
xoxo
xoxo
xoxo
First argument of the method will define amount of characters used to create a square side, second which character is first.
This is my solution but im wondering if i can do it with less code.
static void square(char a, int b) {
if (a == 'x') {
for (int i = 0; i < b; i++) {
int sum = 0;
do {
System.out.print("x");
sum++;
if (sum == b)
break;
System.out.print("o");
sum++;
}
while (sum != b);
System.out.println();
}
} else {
for (int i = 0; i < b; i++) {
int sum = 0;
do {
System.out.print("o");
sum++;
if (sum == b)
break;
System.out.print("x");
sum++;
}
while (sum != b);
System.out.println();
}
}
}
How to make pattern to look like
xoxox
oxoxo
xoxox
oxoxo
xoxox
And how to make this using only for loops or arrays.
O(n^2) loop and constant O(k) space. Start from 0 and just keep alternating the characters till you reach the end (b^2).
char oth = (a == 'x') ? 'o' : 'x';
for (int i = 0; i < (b * b); i++) {
System.out.print(i % 2 == 0 ? a : oth);
if ((i + 1) % b == 0) {
System.out.println();
}
}
O(n) loop and O(n) space. Construct the two pattern rows to print and alternate them.
char oth = (a == 'x') ? 'o' : 'x';
String x = (a == 'x') ? "xo" : "ox";
// Construct the two repeating patterns which will alternate
String first = String.join("", Collections.nCopies(b / 2, x));
String second = first;
if (b % 2 == 1) {
second = new StringBuilder(first).reverse().toString();
first += a;
second += oth;
}
for (int i = 0; i < b; i++) {
System.out.println(i % 2 == 0 ? first : second);
}
See output here: https://ideone.com/E3bVFK
Have fun
public static void square(int side, char order){
char x = (order=='x')?'x':'o';
char o = (order=='x')?'o':'x';
for ( int i = 0; i < side; i++ ) {
for ( int j = 0; j < side; j++ )
System.out.print((j%2==0)?x:o);
System.out.println();
}
}
Output would be exactly what you were asking for.

Categories

Resources