Zigzag conversion - java

Question is : The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility)
P A H N
A P L S I I G
Y I R
And then read line by line: "PAHNAPLSIIGYIR"
I have written below code, appearantly it works fine, but I might miss some corner cases. Could you help me to find all the corner cases for this question on my answer?
public static String zigZagConversion(String s , int rowNum){
if (s == null){
throw new IllegalArgumentException();
}
if (rowNum == 1){
return s;
}
StringBuilder str = new StringBuilder();
int step = 2 * rowNum - 2 ;
for (int i = 0 ; i < rowNum ; i++){
if( i == 0 || i == rowNum -1){
for (int j = i ; j < s.length() ; j +=step){
str.append(s.charAt(j));
}
}
else{
int step2 = 2* (rowNum - i - 1);
int step3 = step - step2;
int k = i;
boolean flag = true;
while (k < s.length()){
str.append(s.charAt(k));
if(flag){
k += step2;
flag = false;
}
else{
k +=step3;
flag = false;
}
}
}
}
return str.toString();
}

It gives incorrect output for "PAYPALISHIRING", 4
P I N
A L S I G
Y A H R
P I
So the correct answer should be PINALSIGYAHRPI.
But your program gives PINALIGYAIHRNPI:
an "S" is missing, one extra "I" and one extra "N".
Your revised version is still incorrect, it gives PINALSIIGYAHNPI.
The problem is in the while loop in the middle.
You need to alternate the step counting,
setting the flag on and off.
Your mistake was to only set it off once, and never back on again.
str.append(s.charAt(k));
if (flag) {
k += step2;
flag = false;
} else {
k += step3;
flag = true;
}
With this correction, I believe your solution is correct. (I also added a minor improvement there, extracting the common str.append(s.charAt(k)); from the if-else branches.

My solution on leetcode forum:
https://leetcode.com/problems/zigzag-conversion/discuss/549451/Java-Solution-O(n)-with-algorithm
The mathematic algorithm for zigzag is:
originalDiff = numRows * 2 - 2;
If -> 'currRow' equals First or last lines
use the originalDiff (numRows * 2 - 2)
Else ->
For each new line:
upperDiff += 2,
lowerDiff -=2
Examples:
numRows =2 -> originalDiff = 2
PYAIHRN
APLSIIG
3 -> 4
P A H N
A P L S I I G
Y I R
numRows = 4 -> originalDiff = 6
P I N
A L S I G
Y A H R
P I
numRows = 5 -> originalDiff = 8
P H
A SI
Y I R
P L I G
A N
*/
My solution:
class Solution {
public String convert(String s, int numRows) {
if(numRows == 1) {
return s;
}
String newString = "";
int originalDiff = numRows * 2 - 2;
int diff = originalDiff;
int upperDiff = 0;
boolean isGoingDown = true;
int currIndex = 0;
int currRow = 0;
int startingIndex = 0;
for(int i = 0; i < s.length(); i++) {
System.out.println(currIndex);
newString += s.charAt(currIndex);
if(currRow == 0 || currRow == numRows - 1) {
currIndex += originalDiff;
} else {
if(isGoingDown) {
currIndex += diff;
isGoingDown = !isGoingDown;
} else {
currIndex += upperDiff;
isGoingDown = !isGoingDown;
}
}
if(currIndex >= s.length()) {
currRow++;
diff -= 2;
upperDiff += 2;
currIndex = currRow;
isGoingDown = true;
}
if(currRow == numRows) {
i = s.length();
}
}
return newString;
}
}

Zigzag conversion from leetcode in Javascript
Solution
const zigzag = (str, num) => {
if (num === 1) {
return str;
}
let check = true;
let result = [];
let i = 0;
while (i < str.length) {
result.push([]);
let j = 0;
while (j < num) {
if (check){
result[result.length-1].push(str[i]);
i++;
} else {
if (j == 0) {
result[result.length-1].push(null);
} else if (j === num-1) {
result[result.length-1].unshift(null);
} else {
result[result.length-1].unshift(str[i]);
i++;
}
}
j++;
}
check = !check;
}
let zigzag = [];
for (let k = 0; k < num; k++){
for(let l = 0; l < result.length; l++) {
zigzag.push(result[l][k]);
}
}
return zigzag.join("");
}
Example Input
zigzag("ABCD", 3)
Output
ABDC
Run
https://repl.it/#VinitKhandelwal/zigzag-conversion-javascript

Using HashMap
public String convert(String s, int numRows) {
if (numRows == 1){
return s;
}
StringBuilder result = new StringBuilder();
Map<Integer, StringBuilder> map = new HashMap<>();
for (int i = 0; i < numRows; i++) {
map.put(i,new StringBuilder());
}
int it = 0;
boolean flip = true;
for (int i = 0; i < s.length(); i++) {
if (flip) {
if(it<s.length()){
map.get(it).append(s.charAt(i));
it++;
}
} else {
map.get(it).append(s.charAt(i));
it--;
}
if (it + 1 == numRows || it == 0)
flip = !flip;
}
for (Map.Entry entry: map.entrySet()) {
result.append(entry.getValue());
}
return result.toString();
}

My Solution is traversing the string in the same way it is said in the problem, it is better to make string array of size numrows and the rest is storing the string character as it is in the logic,
you can keep the index and when that index is 0 i.e at the starting then we have to go till the end of the row and then except for first and last row, every array will have diagonal element.
So after traversing till the end then assign index = numrows - 2 and save in the respective array string and decrease and do the same till index >0 and then again traverse till the end row, do this and when we reach the end of the string then break from the loop.
and then concate all the string of string array in a new res string.
class Solution {
public String convert(String s, int n) {
if(n==1 || n>=s.length())
return s;
String[] a = new String[n]; //string array
int ind=0; // index for the string array
boolean flag=true;
int cnt=0; //to keep the counter till where we have traversed the string
while(true && flag)
{
if(ind==0)
{
for(int i=0;i<n;i++)
{
a[i] += s.charAt(cnt);
cnt++;
if(cnt==s.length())
{
flag=false;
break;
}
} // here it has reached the end so we assign here
ind = n-2;
}
else if(ind>0 && ind<n && flag)
{
a[ind] += s.charAt(cnt);
cnt++;
if(cnt==s.length())
{
flag=false;
break;
}
ind--; // to move diagonally up
}
}
String res = new String("");
for(int i=0;i<a.length;i++)
{
// System.out.println(a[i].substring(4));
res += a[i].substring(4);
}
return res;
}
}

Following is the simple solution.
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows <= 1:
return s
res = ""
p = numRows * 2 - 2
temp = p
for i in range(0,numRows):
index = i
flag = 0
while index < len(s):
res = res + s[index]
if i == 0 or i == numRows-1:
index = index + p
else:
if flag == 0:
index = index + temp
flag = 1
else:
index = index + p-temp
flag = 0
temp = temp - 2
return res

zigzag-conversion Complete JavaScript-based solution
Created an Array of an array of row lengths. The main motive is to arrange characters in 2D array form and concat string row-wise.
var convert = function(s, numRows) {
let array =[],c=0,str='';
for(let row =0; row<numRows ; row++) {
array[row] = new Array();
}
while(c < s.length) {
for(let row =0; row<numRows ; row++) {
if((row+1)%numRows ==0) {
array[row].push(s[c]);
c++;
break;
} else {
array[row].push(s[c]);
c++;
}
}
for(let rr = numRows-2 ; rr>0;rr--) {
array[rr].push(s[c]);
c++;
}
}
for(let row =0; row<numRows ; row++) {
for(let i=0;i<array[row].length;i++){
if(array[row][i]){
str+=array[row][i]
}
}
}
return str
};
convert("PAYPALISHIRING",3)

Related

How to continue 2 loops at the same time? [duplicate]

This question already has answers here:
Continue at first loop , inside the second loop
(7 answers)
Closed 1 year ago.
For this code kata I need to continue 2 for loops at the same time. How can I do that?
public class StringMerger {
public static boolean isMerge(String s, String part1, String part2) {
StringBuilder MergedWord = new StringBuilder("");
String Whole = part1+part2;
for(int j = 0; j < Whole.length(); j++){
for(int I = 0; I < part1.length(); I++){
if((Character.compare(s.charAt(j), part1.charAt(I)) == 0) && (j == I)){
MergedWord.append(s.charAt(I)+"");
continue;
}
else
break;
}
for(int i = 0; i < part2.length(); i++){
if((Character.compare(s.charAt(j), part2.charAt(i)) == 0) && (j == i)){
MergedWord.append(part2.charAt(i) + "");
continue;
}
else
break;
}
}
return s.equals(MergedWord.toString())? true : false;
}
}
I noticed when one of the for loops continue it only continues the internal loop, but labels would continue the upper loop and would be inefficient. Could I continue 2 for loops at the same time a.k.a continue the inner and upper loop in this nested for loop?
The task is to match each character in the target string, so you only need one loop, which iterates over the characters in the string. You do need two other loop variables to track how much of the two source strings have been used up.
My solution below is looping over three things at once: the target string and the two 'part' strings. The rate it moves over the 'part' strings isn't constant, but it does progress over them monotonically.
It wasn't clear to me whether the source strings could include extra characters not used in the target string. As the example didn't show any, I assumed not.
public class MergedStringChecker {
public static void main(String[] args) {
String target = "codewars";
String part1 = "cdw";
String part2 = "oears";
for (int i = 0, p1 = 0, p2 = 0; i < target.length(); ++i) {
if (p1 < part1.length() && target.charAt(i) == part1.charAt(p1)) {
++p1;
} else if (p2 < part2.length() && target.charAt(i) == part2.charAt(p2)) {
++p2;
} else {
throw new RuntimeException("No matching characters at index " + i);
}
}
}
}
The above solution is not Unicode safe if the string contains > 16 bit code points.
Try this.
public static boolean isMerge(String s, String part1, String part2) {
int length = s.length();
int length1 = part1.length();
int length2 = part2.length();
int i1 = 0, i2 = 0;
for (int i = 0; i < length; ++i)
if (i1 < length1 && s.charAt(i) == part1.charAt(i1))
++i1;
else if (i2 < length2 && s.charAt(i) == part2.charAt(i2))
++i2;
else
return false;
return i1 == length1 && i2 == length2;
}
public static void main(String[] args) throws IOException {
System.out.println(isMerge("codewars", "cdw", "oears"));
System.out.println(isMerge("codewars", "codewars", ""));
System.out.println(isMerge("codewars", "cdw", "oearsEXTRA"));
}
output:
true
true
false
you have an error on the first part loop you must append from the part not the s char
you don't need to chek the id of the part with the inital word (remove this check (j == I) and this one (j == i)
Use counter to avoid looping many times
Try this code :
public static boolean isMerge(String s, String part1, String part2) {
StringBuilder MergedWord = new StringBuilder("");
String Whole = part1+part2;
int counter1=0;
int counter2=0;
for(int w = 0; w < Whole.length(); w++){
for(int c1 = counter1; c1 < part1.length(); c1++){
if((s.charAt(w) == part1.charAt(c1)) ){
MergedWord.append(part1.charAt(c1)+"");
counter1++;
continue;
}
else
break;
}
for(int c2 = counter2; c2 < part2.length(); c2++){
if((s.charAt(w) == part2.charAt(c2)) ){
MergedWord.append(part2.charAt(c2) + "");
counter2++;
continue;
}
else
break;
}
}
return s.equals(MergedWord.toString())? true : false;
}

Each substring of a certian length of a binary substring should have at least one '1' character

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;
}

Count maximum number of thieves that can be caught [duplicate]

This question already has answers here:
Algorithm - Police and thief in Grid(N*N)
(2 answers)
Closed 4 years ago.
The question goes like this:
There is a N * N grid.
Each cell of the grid can have either a police denoted by P or a thief denoted by T.
A police can catch a thief only if they are in a same row.
A police can catch only one thief.
A police cannot catch a thief who is more than K units away from it.
Count maximum number of thieves that can be caught.
Example:
T P T
P T P
T T P
Output : 3
Approach:
The approach I have used is, a police needs to catch the thief farthest available from him in the given range i.e k. I have implemented this approach using 2 queues, one for policemen and other for thieves.
Out of 24 test-cases 18 got passed, rest got wrong answer.
If the approach is wrong I would like to know the correct answer.
And if some test case I am missing I would like to know that.
Below you can find my code snippet:
CODE:
import java.util.*;
public class policemenAndThief
{
public int maxThiefCaught(char[][] mat,int k)
{
int count = 0;
ArrayDeque<Integer> police = null;
ArrayDeque<Integer> thief = null;
int n = mat.length;
for(int i = 0 ; i < n ; i++)
{
police = new ArrayDeque<>(n);
thief = new ArrayDeque<>(n);
for(int j = 0 ; j < n ; j++)
{
if(mat[i][j] == 'T')
{
while(police.isEmpty() == false && j - police.peekFirst() > k)
{
police.pollFirst();
}
if(police.isEmpty())
{
thief.addLast(j);
}
else
{
police.pollFirst();
count++;
}
}
else
{
while(thief.isEmpty() == false && j - thief.peekFirst() > k)
{
thief.pollFirst();
}
if(thief.isEmpty())
{
police.addLast(j);
}
else
{
thief.pollFirst();
count++;
}
}
}
}
return count;
}
}
Using Linked list:
int count = 0;
for (int i = 0; i < N; i++) {
String row = N_A[i];
LinkedList<Integer> policeList = new LinkedList<>();
LinkedList<Integer> thiefList = new LinkedList<>();
for (int j = 0; j < N; j++) {
char c = row.charAt(j);
int jk = j + 1;
if (c == 'T') {
boolean add = true;
while (!policeList.isEmpty()) {
int p = policeList.pollFirst();
if ((jk - p) <= k) {
count++;
add = false;
break;
}
}
if (add) {
thiefList.add(jk);
}
} else {
boolean add = true;
while (!thiefList.isEmpty()) {
int t = thiefList.pollFirst();
if ((jk - t) <= k) {
count++;
add = false;
break;
}
}
if (add) {
policeList.add(jk);
}
}
}
}
System.out.println(count);
C++ Solution:
int solution (vector<vector<char> > A, int K) {
int res=0;
for(int i=0;i<A.size();i++)
{
vector<int> pol;
vector<int> thi;
for(int j=0;j<A[0].size();j++)
{
if (A[i][j] == 'P')
pol.push_back(j);
else if (A[i][j] == 'T')
thi.push_back(j);
}
// track lowest current indices of
// thief: thi[l], police: pol[r]
int l = 0, r = 0;
while (l < thi.size() && r < pol.size()) {
if (abs(thi[l] - pol[r]) <= K) {
res++;
l++;
r++;
}
// increment the minimum index
else if (thi[l] < pol[r])
l++;
else
r++;
}
pol.clear();
thi.clear();
}
return res;
}
It passes all 24 test cases.

Need help to solve the hackerrank challenge

I'm trying to solve an "Almost Sorted" challenge in hackerrank the problem is:
Given an array with elements, can you sort this array in ascending order using only one of the following operations?
Swap two elements.
Reverse one sub-segment.
Input Format
The first line contains a single integer, , which indicates the size of the array.
The next line contains integers separated by spaces.
Sample Input #1
2
4 2
Sample Output #1
yes
swap 1 2
Sample Input #2
3
3 1 2
Sample Output #2
no
Sample Input #3
6
1 5 4 3 2 6
Sample Output #3
yes
reverse 2 5
I tried to solve the challenge and my code is working but it seems it's to slow for big arrays.
Kindly asking you to help me to find a better solution for mentioned problem.
Below is my code:
import java.util.*;
public class Solution
{
private static int[] arr;
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
int N = in.nextInt();
arr = new int[N];
for (int i = 0; i < N; i++)
{
arr[i] = in.nextInt();
}
if (IsSorted(arr))
{
System.out.println("yes");
return;
}
if(CheckSingleSwap(arr))
return;
if(CheckSingleReverse(arr))
return;
System.out.println("no");
}
private static boolean CheckSingleReverse(int[] arr)
{
int length = arr.length;
int limit = length - 2;
int current = 1;
List<Integer> indexes = new ArrayList<Integer>();
while (current < limit)
{
for (int i = 0; i < length; i++)
{
int temp = current + i;
for (int j = i; j <= temp && temp < length; j++)
{
indexes.add(j);
}
if (IsSorted(ReverseArrayPart(arr, indexes)))
{
System.out.println("yes");
System.out.println("reverse " + (indexes.get(0) + 1) + " " + (indexes.get(indexes.size() - 1) + 1));
return true;
}
indexes.clear();
}
current++;
}
return false;
}
private static int[] ReverseArrayPart(int[] arr, List<Integer> indexes)
{
int[] result = new int[arr.length];
int[] arrayPart = new int[indexes.size()];
int j = 0;
for (int i = 0; i < arr.length; i++)
{
if (indexes.contains(i))
{
arrayPart[j] = arr[i];
j++;
}
result[i] = arr[i];
}
for(int i = 0; i < arrayPart.length / 2; i++)
{
int temp = arrayPart[i];
arrayPart[i] = arrayPart[arrayPart.length - i - 1];
arrayPart[arrayPart.length - i - 1] = temp;
}
j = 0;
for (int i = 0; i < result.length; i++)
{
if (indexes.contains(i))
{
result[i] = arrayPart[j];
j++;
}
}
return result;
}
private static boolean CheckSingleSwap(int[] arr)
{
int count = 0;
int[] B = Arrays.copyOf(arr, arr.length);
Arrays.sort(B);
List<Integer> indexes = new ArrayList<Integer>();
for(int i = 0; i < arr.length; i++)
{
if(arr[i] != B[i])
{
count++;
indexes.add(i+1);
}
}
if(count > 2)
return false;
System.out.println("yes");
System.out.println("swap " + indexes.get(0) + " " + indexes.get(1));
return true;
}
private static boolean IsSorted(int[] arr)
{
int length = arr.length;
for (int i = 0; i < length - 1; i++)
{
if (arr[i] > arr[i + 1])
{
return false;
}
}
return true;
}
}
For the following code, pass in A as the original array and B as the sorted array.
CheckSingleSwap:
Instead of adding the indices to another list, store the first swap you encounter, and keep going; if you find the corresponding other swap, then store it and record the finding; if you find a different swap, exit with false. At the end if you've recorded the finding, print the corresponding indices.
private static boolean CheckSingleSwap(int[] A, int[] B)
{
int L = A.length;
int firstSwap = -1, secondSwap = -1;
for(int i = 0; i < L; i++)
{
if(A[i] != B[i])
{
if (firstSwap == -1)
firstSwap = i;
else if (secondSwap == -1 && A[i] == B[firstSwap] && A[firstSwap] == B[i])
secondSwap = i;
else
return false;
}
}
if (firstSwap != -1 && secondSwap != -1)
{
System.out.println("yes");
System.out.println("swap " + (firstSwap + 1) + " " + (secondSwap + 1));
return true;
}
System.out.println("array is already sorted!");
return false; // or whatever you decide to do; maybe even an exception or enumerated type
}
CheckSingleReverse:
You are doing WAY too much here! You seem to be brute forcing every single possible case (at first glance).
What you can do instead is to find the region where all the numbers are different. If there are more than two of these, or two which are separated by more than one element, then return false immediately.
The reason for the "more than one" thing above is because of odd-number length regions - the middle element would be the same. If you find such two regions, treat them as one. Then you can proceed to find out if the region is reversed.
private static boolean CheckSingleReverse(int[] A, int[] B)
{
// find region
int L = A.length;
int diffStart = -1, diffEnd = -1; boolean mid = false, found = false;
for (int i = 0; i < L; i++)
{
if (A[i] != B[i])
{
if (found)
{
if (i - diffEnd == 2 && !mid)
{
mid = true;
found = false;
diffEnd = -1;
}
else
return false;
}
else if (diffStart == -1)
diffStart = i;
}
else
if (diffStart != -1 && diffEnd == -1)
{
found = true;
diffEnd = i - 1;
}
}
if (diffEnd == -1)
{
if (A[L - 1] != B[L - 1])
diffEnd = L - 1;
else if (!found)
{
System.out.println("array is already sorted!");
return false;
}
}
// find out if it's reversed
int count = (diffEnd - diffStart + 1) / 2;
for (int i = 0; i < count; i++)
{
int oneEnd = diffStart + i, otherEnd = diffEnd - i;
if (!(A[oneEnd] == B[otherEnd] && A[otherEnd] == B[oneEnd]))
return false;
}
System.out.println("yes");
System.out.println("reverse " + (diffStart + 1) + " " + (diffEnd + 1));
return true;
}
Just to give you an idea of the performance boost, on ideone.com, with an array length of 150, the original implementation of CheckSingleReverse took 1.83 seconds, whereas the new one took just 0.1 seconds. With a length of 250, the original actually exceeded the computational time limit (5 seconds), whereas the new one still took just 0.12 seconds.
From this it would seem that your implementation takes exponential time, whereas mine is linear time (ignoring the sorting).
Funnily enough, with an array size of 3 million I'm still getting around 0.26 seconds (ideone's execution time fluctuates a bit as well, probs due to demand)

Find the longest sequence of same characters in a string

This code supposed to output the longest run on which a character in a string has a consecutive runs of itself. Though the problem is that it outputs: 8 (which should be 5 instead). I just would like to ask what seems to be the problem regarding this code.
public class Sample {
public static void main(String[] args) {
String setofletters = "aaakkcccccczz"; /* 15 */
int output = runLongestIndex(setofletters);
System.out.println("Longest run that first appeared in index:" + output);
}
public static int runLongestIndex(String setofletters) {
int ctr = 0;
int ctrstor = 0;
int ii = 0;
int output = 0;
// loops until the last character in the string
for (int i = 0; i < setofletters.length() - 1; i++) {
// checks if the letter is the same to the next
if (setofletters.charAt(i) == setofletters.charAt(i++)) {
ctr++;
ii = i++;
// loops until the letter in the index is no longer equal
while (setofletters.charAt(i) == setofletters.charAt(ii)) {
ii++;
ctr++;
}
if (ctr > ctrstor) {
output = i;
}
// storing purposes
ctrstor = ctr;
}
// resets the counter
ctr = 0;
}
return output;
}
}
UPDATE Sorry, I misunderstood your question a bit, you need to make the following changes in your code to make it work.(lines with comments)
public static int runLongestIndex(String setofletters){
int ctr = 1; // every character is repeated at least once, so you should initialize it to 1, not 0
int ctrstor = 0;
int ii = 0;
int output = 0;
for (int i = 0; i < setofletters.length() - 1; i++) {
if (i < setofletters.length() - 1 && setofletters.charAt(i) == setofletters.charAt(i+1)) { // i++ is not same as i+1
ctr++;
ii = i+1; // i++ is not same as i+1
while (setofletters.charAt(i) == setofletters.charAt(ii)) {
ii++;
ctr++;
}
if (ctr > ctrstor) {
output = i;
}
ctrstor = ctr;
}
ctr = 1; // for the same reason I mentioned above
}
return output;
}
EDIT : the easiest way to write your code is :
public static int runLongestIndex(String setofletters){
int ctr = 1;
int output = 0;
int j=0;
for(int i=0; i<setofletters.length()-1;i++){
j=i;
while(i <setofletters.length()-1 && setofletters.charAt(i)==setofletters.charAt(i+1)){
i++;
ctr++;
}
if(ctr>output){
output=j;
}
ctr = 1;
}
return output;
}
Why are you assigning i to output? You should assign ctr to output.
change
if(ctr>ctrstor){
output=i;
}
to
if(ctr>ctrstor){
output=ctr;
}
and also I think you should change
if(setofletters.charAt(i)==setofletters.charAt(i++))
to
if(i<setofletters.length()-1 && setofletters.charAt(i)==setofletters.charAt(i+1)){
and you should intialize ctr to 1 but not 0 because every character is repeated at least once.
I'll give you a Scala implementation for that problem.
Here it is the automatic test (in BDD style with ScalaTest)
import org.scalatest._
class RichStringSpec extends FlatSpec with MustMatchers {
"A rich string" should "find the longest run of consecutive characters" in {
import Example._
"abceedd".longestRun mustBe Set("ee", "dd")
"aeebceeedd".longestRun mustBe Set("eee")
"aaaaaaa".longestRun mustBe Set("aaaaaaa")
"abcdefgh".longestRun mustBe empty
}
}
Following is the imperative style implementation, with nested loops and mutable variables as you would normally choose to do in Java or C++:
object Example {
implicit class RichString(string: String) {
def longestRun: Set[String] = {
val chunks = mutable.Set.empty[String]
val ilen = string.length
var gmax = 0
for ((ch, curr) <- string.zipWithIndex) {
val chunk = mutable.ListBuffer(ch)
var next = curr + 1
while (next < ilen && string(next) == ch) {
chunk += string(next)
next = next + 1
}
gmax = chunk.length max gmax
if (gmax > 1) chunks += chunk.mkString
}
chunks.toSet.filter( _.length == gmax )
}
}
}
Following is a functional-style implementation, hence no variables, no loops but tail recursion with result accumulators and pattern matching to compare each character with the next one (Crazy! Isn't it?):
object Example {
implicit class RichString(string: String) {
def longestRun: Set[String] = {
def recurse(chars: String, chunk: mutable.ListBuffer[Char], chunks: mutable.Set[String]): Set[String] = {
chars.toList match {
case List(x, y, _*) if (x == y) =>
recurse(
chars.tail,
if (chunk.isEmpty) chunk ++= List(x, y) else chunk += y,
chunks
)
case Nil =>
// terminate recursion
chunks.toSet
case _ => // x != y
recurse(
chars.tail,
chunk = mutable.ListBuffer(),
chunks += chunk.mkString
)
}
}
val chunks = recurse(string, mutable.ListBuffer(), mutable.Set.empty[String])
val max = chunks.map(_.length).max
if (max > 0) chunks.filter( _.length == max ) else Set()
}
}
}
For example, for the given "aeebceeedd" string, both implementations above will build the following set of chunks (repeating characters)
Set("ee", "eee", "dd")
and they will filter those chunks having the maximum length (resulting "eee").
This code should work for any length of string sequence.
public class LongestStringSequqnce {
static String myString = "aaaabbbbcccchhhhiiiiibbbbbbbbbccccccc";
static int largestSequence = 0;
static char longestChar = '\0';
public static void main(String args[]) {
int currentSequence = 1;
char current = '\0';
char next = '\0';
for (int i = 0; i < myString.length() - 1; i++) {
current = myString.charAt(i);
next = myString.charAt(i + 1);
// If character's are in sequence , increase the counter
if (current == next) {
currentSequence += 1;
} else {
if (currentSequence > largestSequence) { // When sequence is
// completed, check if
// it is longest
largestSequence = currentSequence;
longestChar = current;
}
currentSequence = 1; // re-initialize counter
}
}
if (currentSequence > largestSequence) { // Check if last string
// sequence is longest
largestSequence = currentSequence;
longestChar = current;
}
System.out.println("Longest character sequence is of character "
+ longestChar + " and is " + largestSequence + " long");
}
}
Source : http://www.5balloons.info/program-java-code-to-find-longest-character-sequence-in-a-random-string/
if(ctr>ctrstor){
output=i;
}
//storing purposes
ctrstor=ctr;
This looks like the problem. So if you find 8 consecutive characters, it will set output to 8, and proceed. The next time thru, it finds 3 consecutive characters, so doesn't set output, but sets ctrstor. Next time thru it finds 4 consecutive characters, and this will set output to 4
There are few traps in the code that your logic felt in:
Code incorrectly assumes that there is always next character to compare current one.
This fails for string like "a" or the last character in any string.
Code does not store the max count of characters but only the max index (i).
MaxCount is needed to compare the next chars sequence size.
Loop for and loop while repeat the same subset of characters.
Also variable name style makes it harder to understand the code.
After correcting above
public static int runLongestIndex(String setofletters) {
int maxCount = 0;
int maxIndex = 0;
// loops each character in the string
for (int i = 0; i < setofletters.length() - 1; ) {
// new char sequence starts here
char currChar = setofletters.charAt(i);
int count = 1;
int index = i;
while ( (index < setofletters.length() - 1) &&
(currChar == setofletters.charAt(++index)) ) {
count++;
}
if (count > maxCount) {
maxIndex = i;
maxCount = count;
}
i = index;
}
return maxIndex;
}
See Java DEMO
I think you don't need an internal loop:
public static int runLongestIndex(String setofletters) {
if (setofletters == null || setofletters.isEmpty()) {
return -1;
}
int cnt = 1;
char prevC = setofletters.charAt(0);
int maxCnt = 1;
//char maxC = prevC;
int maxRunIdx = 0;
int curRunIdx = 0;
for (int i = 1; i < setofletters.length(); i++){
final char c = setofletters.charAt(i);
if (prevC == c) {
cnt++;
} else {
if (cnt > maxCnt) {
maxCnt = cnt;
//maxC = prevC;
maxRunIdx = curRunIdx;
}
cnt = 1;
curRunIdx = i;
}
prevC = c;
}
if (setofletters.charAt(setofletters.length() - 1) == prevC) {
if (cnt > maxCnt) {
//maxC = prevC;
maxCnt = cnt;
maxRunIdx = curRunIdx;
}
}
return maxRunIdx;
}
and this code:
System.out.println(runLongestIndex("aaakkcccccczz"));
gives you
5
This is how a "colleague" of mine is understanding to write readable code in order to solve this problem, even if this is working :)
public static int count (String str) {
int i = 0;
while(i < str.length()-1 && str.charAt(i)==str.charAt(i+1))
i ++;
return ++i;
}
public static int getLongestIndex(String str){
int output = 0;
for(int i=0, cnt = 1, counter = 0 ; i<str.length() - 1;i += cnt, cnt = count(str.substring(i)), output = (counter = (cnt > counter ? cnt : counter)) == cnt ? i : output);
return output;
}
int indexOfLongestRun(String str) {
char[] ar = str.toCharArray();
int longestRun = 0;
int lastLongestRun = 0;
int index = 0;
for(int i = ar.length-1; i>0; i--){
if(ar[i] == ar[i-1]){
longestRun++;
}else{
if(longestRun > lastLongestRun){
lastLongestRun = longestRun;
longestRun = 0;
index = i;
}
}
}
return index;
Well, the solution a bit depends on the additional requirements. Here is the code which returns the FIRST longest sequence of a repeated character int the given string, meaning if you have a second sequence with the same length you never get it out :(. But still, this is a simple and clear solution here, so good news - it works! :)
string = 'abbbccddddddddeehhhfffzzzzzzzzdddvyy'
longest_sequence = ''
for i in range(len(string)):
is_sequence = True
ch_sequence = ''
while is_sequence:
ch_sequence += string[i]
if i+1 < len(string) and string[i]==string[i+1]:
i += 1
else:
is_sequence = False
if len(ch_sequence) > len(longest_sequence):
longest_sequence = ch_sequence
print (longest_sequence)
#Paolo Angioletti already provided an answer using Scala, but it's more complicated than it needs to be. The idea is not very different from Run-length encoding. Time complexity O(n).
def longestConsecutive(s: String): (Char, Int) = {
Iterator.iterate(('\u0000', 0, 0)) { case (ch, longestRun, i) =>
val run = (i until s.length)
.takeWhile(s(_) == s(i))
.size
if (run > longestRun) (s(i), run, i + run)
else (ch, longestRun, i + run)
}
.dropWhile(i => s.isDefinedAt(i._3))
.take(1)
.map(x => (x._1, x._2))
.next()
}
Tested with:
("s", "ch", "n")
----------------
("", '\u0000', 0),
("a", 'a', 1),
("aabcddbbbea", 'b', 3),
("abcddbbb", 'b', 3),
("cbccca", 'c', 3)
#include <iostream>
#include<algorithm>
using namespace std;
int main() {
string s="abbcccccbbffffffffff";
//cin>>s;
int count=1;
int maxcount=1;
int start=0;
int ps=0;
for (int i=0;i<s.size()-1;i++)
{
if(s.at(i)==s.at(i+1))
{
count +=1;
maxcount=max(maxcount,count);
}
else
{
ps=max(ps,start+count);
count =1;
start=i;
}
}
for(int i=1;i<=maxcount;i++)
{
cout<<s.at(i+ps);
}
// your code goes here
return 0;
}
This is the simplest I can think of and it will print the number of the longest sequenced identical characters in a one line string.
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
scanner.close();
int count = 0;
int curCount = 1;
for (int i = 0; i < s.length() -1; i++) {
if (s.charAt(i) == s.charAt(i + 1)) {
curCount++;
if (curCount > count) {
count = curCount;
}
}else {
if (curCount > count) {
count = curCount;
}
curCount = 1;
}
}
System.out.println(count);
}

Categories