I'm trying to optimize a problem that I have to make it more readable with the same speed optimization. My problem consists in this:
Allowed function: write.c, nothing else.
Write a program that takes two strings and displays, without doubles, the
characters that appear in either one of the strings.
The display will be in the order characters appear in the command line, and
will be followed by a \n.
As you can see, in the main it will take two of your argument strings (argv[1] and argv[2]) into our function (void remove_dup(char *str, char *str2) after is compiling it with GCC. That temporary array will hold the ASCII value of the character after a duplicate is detected. For example, str1 = "hello" and str2 = "laoblc". The expected output will result as "heloabc" using the write function.
However, GCC was complaining because I have an array subscript with my temporary character array filled in with zeroes from the index of my strings. To stop making the compiler complaint, I had to cast the string index as an int to hold the ASCII value inside my temporary array. This will be our checker, which will determine if there is a duplicate in our string depending on the value of the character. Recompiling it again, but this time using warning flags: gcc -Wextra -Werror -Wall remove_dup.c. This is the error that I get:
remove_dup:11 error: array subscript is of type 'char' [-Werror,-Wchar-subscripts]
if (temp[str[i]] == 0)
^~~~~~~
remove_dup:13 error: array subscript is of type 'char' [-Werror,-Wchar-subscripts]
temp[str[i]] = 1;
^~~~~~~
remove_dup:21 error: array subscript is of type 'char' [-Werror,-Wchar-subscripts]
if (temp[str2[i]] == 0)
^~~~~~~~
remove_dup.c:23 error: array subscript is of type 'char' [-Werror,-Wchar-subscripts]
temp[str2[i]] = 1;
^~~~~~~~
Now my real question is, how can I have the same time efficiency BUT without using any kind of casting into my array? This program is running as O(m + n) where m is our first string and n is our second string.
This is the code:
void remove_dup(char *str, char *str2)
{
int temp[10000] = {0};
int i;
i = 0;
while (str[i])
{
if (temp[(int)str[i]] == 0)
{
temp[(int)str[i]] = 1;
write(1, &str[i], 1);
}
i++;
}
i = 0;
while (str2[i])
{
if (temp[(int)str2[i]] == 0)
{
temp[(int)str2[i]] = 1;
write(1, &str2[i], 1);
}
i++;
}
}
int main(int argc, char *argv[])
{
if (argc == 3)
remove_dup(argv[1], argv[2]);
write(1, "\n", 1);
return (0);
}
I hope this is clear enough with the logic structure I explained. I might have grammar mistakes, so bear with me :).
Casting here will have no performance penalty.
However, as a rule of thumb, it is generally best to avoid explicit casts whenever possible. You can do this by for example by changing:
temp[(int)str[i]]
to:
temp[+str[i]]
This will work by the usual arithmetic conversions.
However, your code has another problem. You could ask: why would gcc bother to issue such an annoying warning message?
One answer is that they just like to be annoying. A better guess is that on most platforms char is signed-- see Is char signed or unsigned by default? --and so if your string happen to have an ASCII char greater than 127 (i.e. less than zero), you will have a bug.
One way to fix this is to replace:
temp[(int)str[i]]
with:
temp[str[i] + 128]
(and change int temp[10000] = {0} to int temp[256 + 128] = {0}). This will work regardless of the default sign of char.
Now my real question is, how can I have the same time efficiency BUT without using any kind of casting into my array?
I don't believe casting in C has a runtime penalty. Everything in C is a number anyway. I believe it's just telling the compiler that yes, you know you're using the wrong type and believe it's ok.
Note that char can be signed. It is possible for a negative number to sneak in there.
This program is running as O(m * n) where m is our first string and n is our second string.
No, it's running as O(n). O(m*n) would be if you were iterating over one string for every character of the other.
for( int i = 0; i < strlen(str1); i++ ) {
for( int j = 0; j < strlen(str2); j++ ) {
...
}
}
But you're looping over each string one after the other in two independent loops. This is O(m + n) which is O(n).
On to improvements. First, temp only ever needs to hold the char range which is, at most, 256. Let's give it a variable name that describes what it does, chars_seen.
Finally, there's no need to store a full integer. Normally we'd use bool from stdbool.h, but we can define our own using signed char which is what stdbool.h is likely to do. We're sure to wrap it in an #ifndef bool so we use the system supplied one if available, it will know better than we do what type to use for a boolean.
#ifndef bool
typedef signed char bool;
#endif
bool chars_seen[256] = {0};
You might be able to get a bit more performance by eliminating i and instead increment the pointer directly. Not only more performance, but this makes many string and array operations simpler.
for( ; *str != '\0'; str++ ) {
if( !chars_seen[(size_t)*str] ) {
chars_seen[(size_t)*str] = 1;
write(1, str, 1);
}
}
Note that I'm casting to size_t, not int, because that is the proper type for an index.
You might be able to shave a touch off by using post-increment, whether this helps is going to depend on your compiler.
if( !chars_seen[(size_t)*str]++ ) {
write(1, str, 1);
}
Finally, to avoid repeating your code and to extend it to work with any number of strings, we can write a function which takes in the set of characters seen and displays one string. And we'll give the compiler the hint to inline it, though it's of questionable use.
inline void display_chars_no_dups( const char *str, bool chars_seen[]) {
for( ; *str != '\0'; str++ ) {
if( !chars_seen[(size_t)*str]++ ) {
write(1, str, 1);
}
}
}
Then main allocates the array of seen characters and calls the function as many times as necessary.
int main(int argc, char *argv[]) {
bool chars_seen[256] = {0};
for( int i = 1; i < argc; i++ ) {
display_chars_no_dups( argv[i], chars_seen );
}
write(1, "\n", 1);
}
Related
I have two strings and need to compare them AND state which characters are correct.
My code is:
char Palavra = "exemplo";
int Tamanho = strlen(Palavra),i;
printf("Which is your guess?\n");
char Palpite[Tamanho];
fgets(Palpite,Tamanho,stdin);
char Mensagem[50];
strcpy(Mensagem,"The following characters are correct: ");
for(i=0;i<=Tamanho;i++){
if(Palavra[i] == Palpite[i]) sprintf(Mensagem,"%s %i",Mensagem,i+1);
}
printf("%s",Mensagem);
I need to compare variables Palavra and Palpite, character per character and then say with one is correct.
Something like this:
Palavra = celular
Palpite = cilular
Answer should be: "The following characters are correct: 1 3 4 5 6 7"
But I am getting: "The following characters are correct: 1 3 4 5 6 8"
Any help would be greatly appreciated.
Probably it does not work because you have assigned a pointer to an array to a char value.
Also it could be that you are stepping out of array bounds with for i<=Tamnho is causing some comparison problems/possibly crashes (if you are trying to change them).
But the logic behind the code is correct, so I don't see any other reason as to why it does not work
Maybe this is the issue:
int Tamanho = strlen(Palavra),i;
...
for(i=0;i<=Tamanho;i++){
if(Palavra[i] == Palpite[i]) ... do something
}
Palvara[Tamanho] is going to be a null char. Valid indices for Palvara with a non-null character are from [0..Tamanho-1]. This corrects the bug:
for(i=0; i < Tamanho; i++){
if(Palavra[i] == Palpite[i]) ... do something
}
Also, don't forget that Palpite's length might be less than Palvara's length. Make sure you break out of your for-loop before dereferencing Palpite[i] if i >= strlen(Palpite)
Better:
int len = strlen(Palpite);
int count = (Tamanho <= len) ? Tamanho : len;
for(i=0; i < count; i++){
if(Palavra[i] == Palpite[i]) ... do something
}
It worked as expected with this code, although that last if is a clear work around I am not really confortable using.
int Tamanho = strlen(Palavra),i;
printf("Which is your guess?\n");
char Palavra = "exemplo";
char Palpite[Tamanho];
fgets(Palpite,Tamanho,stdin);
char Mensagem[50];
strcpy(Mensagem,"The following characters are correct: ");
for(i=0; i<=Tamanho; i++) {
if(Palavra[i] == Palpite[i]) {
if(i==Tamanho) {
sprintf(Mensagem,"%s %i",Mensagem,i);
} else {
sprintf(Mensagem,"%s %i",Mensagem,i+1);
}
}
}
printf("%s",Mensagem);
Code incurs undefined behavior (UB) due to attempting to access outside an array's range.
When i==Tamanho, Palpite[i] is bad.
char Palpite[Tamanho];
for(i=0;i<=Tamanho;i++){
if(Palavra[i] == Palpite[i]) sprintf(Mensagem,"%s %i",Mensagem,i+1);
}
Code incurs undefined behavior (UB) due to attempting to access access and write to the same array with int sprintf(char * restrict s, const char * restrict format, ...);. The restrict is a contract that the caller broke by using the destination as an later augment.
sprintf(Mensagem,"%s %i",Mensagem,i+1);
// ^^^^^^^^ ^^^^^^^^ UB
Alternative code could have written to the end.
size_t len = strlen(Mensagem);
sprintf(Mensagem + len," %i",i+1);
Better code would use snprintf() to issue no buffer overrun.
A well warning enabled compiler would have warned about the type mismatch. Save time, enable all warnings.
char Palavra = "exemplo";
UB for writing outside arrays bounds. char Mensagem[50]; is too small for "The following characters are correct: 1 3 4 5 6 8" which needs 51.
Consider scaling Mensagem[]; with a more geneorus size and use snprintf() to avoid overflow.
This question already has answers here:
How do I determine the size of my array in C?
(24 answers)
Closed 3 years ago.
Is there a way, I can use of strlen() to find the length of the arrays, instead of specifying in the loop.
First Code: Working With x < 4 Which is the size of the array of temps[4].
#include <stdio.h>
#include <string.h>
int main(){
float temps[4] = {72.5, 73.4, 74.7, 75.2};
int x;
printf("Local Temperatures\n");
for(x = 1; x < 4; x++){
printf("Station %d: %.1f\n",x,temps[x]);
}
}
My Second Code, Not Working, But Looking At What, I Am Trying To achieve with strlen() to find the size of the array.:
#include <stdio.h>
#include <string.h>
int main(){
float temps[4] = {72.5, 73.4, 74.7, 75.2};
int x;
float size;
size = temps;
printf("Local Temperatures\n");
for(x = 1; x < strlen(size); x++){
printf("Station %d: %.1f\n",x,size[x]);
}
}
strlen is effectively an optimized version of the following:
size_t len = 0;
const char *p = s;
while (!*p) {
++len;
++p;
}
You can easily adapt that.
size_t len = 0;
const float *p = s;
while (*p != 0.0) {
++len;
++p;
}
Of course, that means you need a sentinel value just like you had for the string.
float temps[] = { 72.5, 73.4, 74.7, 75.2, 0.0 };
While you can use a value other than 0.0 as your sentinel value, using a sentinel value might not be desirable, so you might not really want to take the same approach as one does for strings.
For an array (as opposed to a pointer), you can use the following to determine the number of elements in the array:
sizeof(a)/sizeof(*a)
That means you can use the following to determine the number of elements in temps:
sizeof(temps)/sizeof(*temps)
Simple method is to get the size of a array is by sizeof(temps)/sizeof(temps[0])
something like,
for(x = 0; x < sizeof(temps)/sizeof(temps[0]); x++){
printf("Station %d: %.1f\n",x,temps[x]);
}
Note: In your snippet array access is done from 1 to (size) instead 0 to (size-1) which is out of bound access.
Short answer: no.
strlen expects an argument of type char *, which points to the first character of a string, which is a sequence of character values including a zero-valued terminator. strlen returns the number of characters before the terminator:
/**
* A naive implementation of strlen. Actual implementations
* are a little more clever.
*/
size_t strlen( const char *str )
{
size_t count = 0;
while( str[count] )
count++;
return count;
}
The important thing to remember is that strlen returns the length of the string, not the size of the array containing the string. If you have something like
char foo[1024] = “bar”;
the strlen( foo ); returns 3.
If you tried to pass an integer or floating point array to strlen the compiler would yell at you because of the wrong argument type. You could work around this by casting the argument to char *, but you would still likely get a wrong answer, not only because integer and float types are multiple chars wide, but also because such a value may have an embedded zero-valued byte. For example, the integer value 16 is represented as the bytes 0x00, 0x00, 0x00, 0x10. If that’s the first element of your integer array, then strlen would return 0 on a big-endian platform and 1 on a little-endian platform.
If you defined the array, then you know how big it is. If you’re writing a function that receives an array argument, then you must either also receive the size as a separate argument, or you must rely on the presence of a sentinel value in the array.
Just do this to get the length of your float array,
int main()
{
float ar[4] = {1.1,1.2,1.3,1.4};
float b;
printf("Array Size : %d\n",sizeof(ar)/sizeof(b));
return 0;
}
You'll get the total number of element of your array , which you can use further in a loop to print the elements of array.
Note: Do not Use sizeof in case of pointers.
strlen() takes a "const char *" type value as argument. So it cannot be used with integer or float arrays.
I'm studying C on my own in preparation for my upcoming semester of school and was wondering what I was doing wrong with my code so far.
If Things look weird it is because this is part of a much bigger grab bag of sorting functions I'm creating to get a sense of how to sort numbers,letters,arrays,and the like! I'm basically having some troubles with the manipulation of strings in C currently.
Also, I'm quite limited in my knowledge of C at the moment!
My main Consists of this:
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
int numbers[10];
int size;
int main(void){
setvbuf(stdout,NULL,_IONBF,0); //This is magical code that allows me to input.
int wordNumber;
int lengthOfWord = 50;
printf("How many words do you want to enter: ");
scanf("%i", &wordNumber);
printf("%i\n",wordNumber);
char words[wordNumber][lengthOfWord];
printf("Enter %i words:",wordNumber);
int i;
for(i=0;i<wordNumber+1;i++){ //+1 is because my words[0] is blank.
fgets(&words[i], 50, stdin);
}
for(i=1;i<wordNumber+1;i++){ // Same as the above comment!
printf("%s", words[i]); //prints my words out!
}
alphabetize(words,wordNumber); //I want to sort these arrays with this function.
}
My sorting "method" I am trying to construct is below! This function is seriously flawed, but I'd thought I'd keep it all to show you where my mind was headed when writing this.
void alphabetize(char a[][],int size){ // This wont fly.
size = size+1;
int wordNumber;
int lengthOfWord;
char sortedWords[wordNumber][lengthOfWord]; //In effort for the for loop
int i;
int j;
for(i=1;i<size;i++){ //My effort to copy over this array for manipulation
for(j=1;j<size;j++){
sortedWords[i][j] = a[i][j];
}
}
//This should be kinda what I want when ordering words alphabetically, right?
for(i=1;i<size;i++){
for(j=2;j<size;j++){
if(strcmp(sortedWords[i],sortedWords[j]) > 0){
char* temp = sortedWords[i];
sortedWords[i] = sortedWords[j];
sortedWords[j] = temp;
}
}
}
for(i=1;i<size;i++){
printf("%s, ",sortedWords[i]);
}
}
I guess I also have another question as well...
When I use fgets() it's doing this thing where I get a null word for the first spot of the array. I have had other issues recently trying to scanf() char[] in certain ways specifically spacing my input word variables which "magically" gets rid of the first null space before the character. An example of this is using scanf() to write "Hello" and getting " Hello" or " ""Hello"...
Appreciate any thoughts on this, I've got all summer to study up so this doesn't need to be answered with haste! Also, thank you stack overflow as a whole for being so helpful in the past. This may be my first post, but I have been a frequent visitor for the past couple of years and it's been one of the best places for helpful advice/tips.
You're going to like this - it's a derivation of QSort, adapted to your situation. It may not work quite perfectly for you without a touchup here or there (TEST FIRST!):
void qsort (Strings[], NumberOfItems)
{
char Temp[51]; // Assumes your max string length of 50
int I1 = 0; // Primary index
int I2 = 0; // Index + 1
int NumberOfItems_1 = 0;
bool Swapped = false;
do // Outer loop
{
Swapped = false;
// Decrement our limit
NumberOfItems--;
// Save time not performing this subtraction many times
NumberOfItems_1 = NumberOfItems - 1;
// Repeatedly scan the list
for (I1 = 0; I1 < NumberOfItems_1; I1++)
{
// Save time not performing this addition many times
// I1 points to the current string
// This points to the next string
I2 = I1 + 1;
// If the current string is greater than the next string in the list,
// swap the two strings
if (strcmp(Strings[I1], Strings[I2]) > 0)
{
strcpy (Temp, Strings[I1]);
strcpy (Strings[I1], Strings[I2]);
strcpy (Strings[I2], Temp);
Swapped = true;
}
}
}
while (Swapped); // Break out when we've got nothing left to swap
}
I see a few things wrong with your code off the bat. First, you declare sortedWords as a multidimensional array (since you have sortedWords[wordnumber][lengthofword], but you try to use it with only one index later on.. this doesn't work! Also, your passing of the 2D array is not valid. Check out this post to see the valid ways to pass a 2D array: Passing a 2D array to a C++ function
Function declaration and definition
The function declaration is invalid, as you've found out. You must specify the size of every dimension of the array except the first, so you might write:
void alphabetize(char a[][SOMESIZE], int size)
However, you have a non-constant second dimension (so you're using a VLA or variable length array), which means that you need to pass both sizes to the function, and pass them before you pass the array:
void alphabetize(int size, int length, char a[size][length])
and then invoke it:
alphabetize(wordNumber, lengthOfWords, words);
Of course, you should also declare the function before you try calling it.
There are probably other issues to be addressed, but this is the immediate one that jumps out. For example, you need to use size and length to control the loops in the function. You probably don't need to copy the data into the local array (in which case you don't need the local array). Etc.
You should consider compiling with options such as:
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
-Wold-style-definition -Wold-style-declaration -Werror …
Note that note all versions of GCC support all those options, but use as many as are supported.
Input issue
You have:
int i;
for (i = 0; i < wordNumber + 1; i++) { //+1 is because my words[0] is blank.
fgets(&words[i], 50, stdin);
}
You're stepping out of bounds of your array, which potentially wreaks havoc on your code. The first entry is blank because scanf() leaves the newline in the input buffer. You should read to the end of line before going to line-based input:
int c;
while ((c = getchar()) != EOF && c != '\n')
;
You should also check the fgets() returns a non-null pointer; don't continue if it does.
I am trying to obfuscate a string in a program. Currently, I only have a simple string reversal working. I would like to be able to perform XOR scrambling on the data to make it much more secure, however the method I have tried is not working.
The same function and input type is used to decode the string. This is no problem with string reversal, as it just reverses back, but can this be done easily with XORing without getting too complex? I would prefer if the process kept just the one string, like the reversal does. Here is my reversal function.
void reverse_string(unsigned char *buf, int length)
{
int i;
unsigned char temp;
for (i = 0; i < length / 2; i++)
{
temp = buf[i];
buf[i] = buf[length - i - 1];
buf[length - i - 1] = temp;
}
}
And here is the attempt at a XOR function
void charxor(char * text, int len) {
const unsigned char enc[8]={173,135,131,121,110,119,187,143};
char ch;
int i;
int ind=0;
for (i=0;i<len;i++) {
ch=*text++;
if (ch)
*text = ch ^ enc[ind++];
ind %=8;
}
}
Can anyone help? Would be much appreciated!
You seem to be overcomplicating things a bit. Try this instead:
void charxor (unsigned char *text, int len) {
const unsigned char enc[8] = {173,135,131,121,110,119,187,143};
int i;
for (i = 0; i < len; i++) {
text[i] ^= enc[i % 8];
}
}
Note that the XOR operation can introduce null chars into the string, so you really do need to keep track of its length instead of just relying on the presence of a trailing null char.
Also keep in mind that, while this may indeed be relatively speaking "much more secure" than just reversing the string, any reasonably clever person with access to enough samples of the output can probably figure out how to decode it in around fifteen minutes or so.
this is a pbox, it would require you to make a non repeating integer key - random - same size as said block. the last block would start with the offset which could be just random data. Doesn't cover null terminators so decide where the data is going / what your doing with it. you could realloc(buff, "A") to use memmove. make 3 64 bit boxes, and a subset of 16 4 bit boxes from the output of the 64 and it starts to look like a poor implementation of des, which openssl has build into it. The fundamental advantage is being able to encrypt/decrypt with the same function / address space. This could also allow you to encrypt in place without a extra buffer. KSZ is the length of your block(s)/key
char
*zecr
(bff, zbf, ky, ze)
char *bff;
char *zbf;
unsigned int ky[];
short ze;
{
/* main encrypt decrypt function */
int i=0;
while( i < KSZ ) {
int dx = ky[i];
if( ze == 1 ) { // encrypt
char c = bff[dx];
sprintf(zbf + i, "%c", c);
} else { // decrypt
char c = bff[i];
char tk[1] = "";
sprintf(tk, "%c", c);
memmove(zbf +dx, tk, 1);
}
i++;
}
return zbf;
}
xoring is a binary operation, which will yield vastly different results depending on how you cast it. You got the right idea using ocdec but if the idea is to keep it simple im going to assume you don't actually know assembly despite the requested reference, stick with c calls its simpler for how you are most likely going to be using the data.
-the female orgasm, that's the myth. -SUN TZU
So, I have a task to find number of substrings in given string. I can't use any C libraries for doing this task. stringExist can only have 2 strings as a parameters.
My solution is working, but I have a feeling that there should be a more elegant way to do this task.
Solution 1: as it turns out, it doesn't work correctly
#include <stdio.h>
int stringExist(char string[], char s2[]);
int main(void){
char string[] = "strstrASDstrSTRst";
char s2[] = "str";
printf("Final result: %i\n",stringExist(string,s2));
return 0;
}
int stringExist(char string[], char s2[]){
/* I am aware that I can init all this values in one row */
int count = 0;
int size = 0;
int i = 0;
int temp = 0;
int result = 0;
while(s2[size]!='\0'){
size++;
}
while(string[i]!='\0')
{
if(string[i]==s2[0])
{
printf("Found first occurrence\n");
count=0;
while((temp=(string[i]==s2[count]))!=0)
{
count++;
if(size==count){
printf("Match\n");
result++;
break;
}
i++;
}
}
i++;
}
return result;
}
Solution number 2:
So far no errors found.
Did a little bit different string traversal, now I don't increment i in the compare chars loop.
#include <stdio.h>
int stringExist(char string[], char s2[]);
int main(void){
char string[] = "bobobobojkhhkjjkhbo;klkl;bobo";
char s2[] = "bobo";
printf("Final result: %i\n",stringExist(string,s2));
return 0;
}
int stringExist(char string[], char s2[]){
int count = 0;
int size = 0;
int i = 0;
int c = 0;
int temp = 0;
int result = 0;
while(s2[size]!='\0'){
size++;
}
for(i=0;string[i]!='\0';i++){
if(string[i]==s2[0])
{
printf("Found first occurence at %i\n",i);
count = 0;
c = i;
while((temp=(string[c]==s2[count]))!=0)
{
printf("Count %i, I %i, current char: %c\n",count, c,string[c]);
count++;
if(size==count){
printf("Match\n");
result++;
break;
}
c++;
}
}
}
return result;
}
Thanks for you suggestions,
Vitaly
beat it: (also works for the extra condition)
int stringExist( char *string, char *sub )
{
int count = 0;
while( *string )
{
char *a = string, *b = sub;
while( *a && *a == *b ) {a++;b++;}
count += !*b;
++string;
}
return count;
}
I suggest writing it the way you would if you were allowed to use library functions. Then go back and write your own versions of those library functions that you used. While writing highly optimized versions of the string.h functions may be difficult, writing decent versions of most of them them in C is pretty easy..
Using subroutines (functions) to preform sub-tasks of this problem will help you to keep your code clear and also avoid certain types of problems, such as if you called:
x = stringExist("aaaaa", "aa");
There are 4 occurances of the string "aa" within "aaaaa", but I don't think that your function will find all of them. The reason for this is that as you search through the larger string for occurances of the second you are using the same index for both the beginning of the string and within the string. In fact, it looks like you would get the wrong results for:
x = stringExist("tBatBath", "tBath");
Unless of course, I've misunderstood what the function was supposed to do.
If you were to write your own version of a string prefix comparing function (essentially memcmp or strncmp) then you would have separated the job of matching the length of the string from looking deeper into the string and probably would not have made such a mistake.
If you are worried about squeezing efficiency out of your functions and the overhead of calling functions, don't. First, it's not that bad. Second, just declare them inline or static inline and if you compile with optimization turned on the compiler will most likely generate code as good as it would have without the use of multiple functions.
This feels like a homework question - in which case you should definitely just do it yourself. But, something that you'll probably want to check for that I don't think your code is handling correctly now is this:
How many times does "bobo" appear in the string "bobobo". It should probably be twice and I think your code as is will only count one.
Good luck,
Mark.
Well, from an algorithmic point of view, it's not bad. You can make optimizations, but I don't think that's the point (looks like homework!).
You may have a slight issue: in a string like "hahahaha", how many times should "haha" be detected? Twice? Thrice? Your code would see it twice.
From a stylistic point of view, there's certainly room for improvement, but you'll learn that over time, from coding and reading other's code =). Keep at it!