简体   繁体   中英

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:

  1. 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.

  2. 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.

  3. Loop for and loop while repeat the same subset of characters.

  4. 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);
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM