Testing Strings

Hi, I put together a program that is supposed to print an alien word called a "blurb." A blurb consists of an whoozit which is an x followed by one or more y. The blurb may or may not be followed by a whatzit, which is a (q and z), or a (q and d), followed by a whoozit.

Examples of blurb:
xy
xyy...y

xyqzxy
xy...yqzxy...y
xyqdxy
xy...yqdxy...y

The program will print a blurb when I enter the last 4, but if I do the first two then the program aborts. I can see that the problem is sending an out of range index to the whatzit function, but I am not sure how to organize my code to avoid this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <string>
using namespace std;

int isWhoozit(string blurb);
int isWhatzit(string, int);

int main()
{
	string check;
	string blurb;

	while (1) {

		cout << "Enter an alien word: " << endl;
		cin >> blurb;
		
		if (isWhoozit(blurb) > 0) {
			cout << "blurb" << endl;
		}
		else cout << "not blurb" << endl;
		cout << isWhoozit(blurb);

	}
}



int isWhoozit(string blurb)
{
	int whoozit = 1;
	int notWhoozit = 0;
	int count_y = 1;

	if (blurb[0] != 'x' || blurb[count_y] != 'y') {
		return notWhoozit;
	}

	while (blurb[0] == 'x' && blurb[count_y] == 'y') {
		count_y++;
	}
	

	if (blurb[count_y] != 'y') {

		if (isWhatzit(blurb, count_y) == 0) {
			return notWhoozit;
		}
	}
	return whoozit = count_y - 1;
}



int isWhatzit(string whatzit, int index)
{
	if ( (whatzit[index] != 'q' && whatzit[index + 1] != 'z') || (whatzit[index] != 'q' && (whatzit[index + 1] != 'd')) ||
		  ((whatzit[index + 2] != 'x') && (whatzit[index + 3] != 'y')) ) {
		return 0;
	}
	return 1;
}  
line 44 assumes there is more word after say xyyy is entered. It does not know to stop -- the count of ys + 1 for x == the strings total length, then its legal and done.

the bool type should be used for true/false code. int can be used but you should use bool, which is exactly 1 or 0 / true or false.
you don't need the 0 and 1 constant names, its readable to see that
iswhoozit ...
return true (ok, I read that it is a whoozit)
return false (ok, I read that it isn't a whoozit)

there are several cute ways to do this problem. This is a good one to revisit as your skills grow, and you see better and better ways to do it.
Last edited on
The blurb may or may not be followed by a whatzit


Do you mean:

The Whoozit may or may not be followed by a whatzit

as the whole aliean word is called a blurb?
For one way, consider (assuming just lowercase and that I've understood the language!):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <string>
#include <string_view>

bool isWhoozit(std::string_view& alien);
bool isWhatzit(std::string_view& alien);
bool isBlurb(std::string_view alien);

int main() {
	std::string blurb;

	while ((std::cout << "Enter an alien word: ") && (std::cin >> blurb))
		std::cout << blurb << " is " << (!isBlurb(blurb) ? "not " : "") << "a blurb\n";
}

bool isWhoozit(std::string_view& alien) {
	if (alien.empty() || alien.front() != 'x')
		return false;

	do alien.remove_prefix(1);
	while (alien.front() == 'y');

	return true;
}

bool isWhatzit(std::string_view& alien) {
	if (alien.size() < 3 || (!alien.starts_with("qd") && !alien.starts_with("qz")))
		return false;

	alien.remove_prefix(2);
	return isWhoozit(alien);
}

bool isBlurb(std::string_view alien) {
	return isWhoozit(alien) && (alien.empty() || isWhatzit(alien));
}

Last edited on
Thanks for pointing out the use of bool jonnin.

Seeplus, thank you for a cleaner implementation. I was curious to see some other ideas on how to implement it. I never heard of string_view before. So it looks like that will allow us to use member functions of string_view to analyze the string, instead of trying to access index values like I was; is that correct? I had Visual Studio 2019 and it didn't recognize it so I upgraded to VS 2022, but the problem persists. Do you know how to get VS to recognize string_view.
you need to set the compiler to c++ 17 or higher in the project settings.

string view is good performance, read this: https://www.nextptr.com/tutorial/ta1217154594/cplusplus-stdstring_view-for-better-performance-an-example-use-case

I can think of several ways to do this problem.
c++ has built in regular expressions, and I believe that is what you have here. Basically x followed by 1 or more y followed by q followed by one of d or z followed by x followed by 1 or more y... you can express that in regex and just let the library do the work.

it is also a finite state machine. this is a convoluted way to code it.

straight iteration is probably the most efficient, just touching each letter one time and failing if it goes wrong else passing at the end. This is a lot like what you have, but just lump it all up, don't try to make extra functions.

you can use string replace to simplify it. if you replace all yy with y until only single ys remain, there are I think 5 or 6 possible results, which you can then check in a lookup table and its done {xy, xyqd, xyqz, xyqdxy, xyqzxy, is that all of them?}

and there are plenty more snarky things to do with the 3 string libraries and c++ tools. But nothing will ever be more efficient than touching each letter one time and pass/fail. Clever, short and clean code can be a nightmare of inefficiency behind the scene ... the tools and libraries hide tons of loops and copies and such if you are not very careful about it. And it does not matter, if you are playing, but its an important thing if you need speed.
Last edited on
Thanks for painting the bigger picture on how library code may look clean but can slow thing down behind the scenes. I'm not worried about speed here, but I do like having the understanding because that helps the thought process when deciding how to implement something.

For the list of valid alien words: xy, xyqdxy, xyqzxy
If there is a qz or a qd it must be followed by x then one or more y.
In any of the 3 examples there can be one y or more y: xyy, xyyyyqdxyy, xyyqzxyyyy

I used your suggestion with .replace(), but I wasn't able to do it all in one loop, and feel that it can be done more efficiently.

I reprogrammed the solution and it seems to work for the most part as I got different combinations of a blurb correct. However, there is a bug that I am not understanding because every once in a while it will print the wrong answer. It seems to be random; one entry might be right at one point but then wrong later.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string blurb;
	int count_y = 1;
	int count_next_set_y = 5;

	while (1) {

		cout << "Enter an alien word: " << endl;
		cin >> blurb;

		if (blurb.length() <= 1) {
			cout << "Not a blurb" << endl;
		}
	    else if (blurb[0] == 'x' && blurb[count_y] == 'y') {

			while (blurb[count_y] == 'y') {
				count_y++;
			}
			cout << "blurb" << endl;
			blurb.replace(1, count_y - 1, "y");
		}
	    else if ((blurb[2] == 'q' && blurb[3] == 'z') && (blurb[4] == 'x' && blurb[5] == 'y')) {

			while (blurb[count_next_set_y] == 'y') {
				count_next_set_y++;
			}
			blurb.replace(5, count_next_set_y - 5, "y");
			cout << "blurb" << endl;
		}
	    else if ((blurb[2] == 'q' && blurb[3] == 'd') && (blurb[4] == 'x' && blurb[5] == 'y')) {

			while (blurb[count_next_set_y] == 'y') {
				count_next_set_y++;
			}
			blurb.replace(5, count_next_set_y - 5, "y");
			cout << "blurb" << endl;
		}
		else cout << "Not a blurb" << endl;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

bool isblurb( const string &test )
{
   return test.size() > 1 && test[0] == 'x' && test[1] == 'y'
       && count(test.begin(),test.end(),'y') == test.size() - 1 - 3 * (test.find("qdxy") != string::npos || test.find("qzxy") != string::npos);
}

int main()
{
   string test;
   cout << "Enter an alien word: ";   cin >> test;
   cout << test << ( isblurb( test ) ? " is " : " is not " ) << "a blurb\n";
}
Last edited on
However, there is a bug that I am not understanding


So use the debugger to trace through the code and watch the contents of the variables. When the trace doesn't work as expected, then you're found a problem.
Okay, I had to come back to it with a fresh set of eyes and rethink it. I reprogrammed the solution and everything seems to be working fine.

jonnin I went with your advice because you were branching off of what I was already trying to do. I had to use two while loops. One to replace the first set of y, with just one y. Then, another loop to simplify the next set of y to just one y. From there I used an if to test for equality. If match blurb, else not a blurb.

lastchance, hats off to you for getting that whole thing in two lines of code. That looks clean!

Thank you everyone for your feedback and examples!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <string>
using namespace std;

int main()
{
	string blurb;

	while (1) {

		int count_y = 1;
		int count_y_again = 5;

		cout << "Enter an alien word: " << endl;
		cin >> blurb;

		if (blurb[0] == 'x' && blurb[1] == 'y') {
			while (blurb[count_y] == 'y') {
				count_y++;
			}
			blurb.replace(1, count_y -1, "y");
		}
		
		if (blurb.length() > 6) {
			while (blurb[count_y_again] == 'y') {
				count_y_again++;
			}
			blurb.replace(5, count_y_again -5, "y");
		}

		if ((blurb == "xy")     ||
			(blurb == "xyqzxy") ||
			(blurb == "xyqdxy")){
			cout << "blurb!" << endl;
		}
		else cout << "Not a blurb" << endl;

		cout << endl;
	}
}
Topic archived. No new replies allowed.