user285372
user285372

Reputation:

Checking two conditions while iterating over an array of characters

Having learned the basics/fundamentals of the C# programming language, I am now trying to tackle my first real-world problem: Write a program that, given a string, finds its longest sub-string that contains at least one upper-case letter but no digits (and then displays the length of this longest sub-string). This could be two qualifying conditions for an acceptable password, for example...

I have written the code below all by myself, which means there is probably performance issues, but that is for later consideration. I am stuck at the point where I have to make sure there is no digit in the sub-string. The comments in my code show my thinking while writing the program...

I thought first I should check to see if there is an upper-case letter in an extracted sub-string, and if there was, then I can store that qualifying sub-string in a list and then break out of the loop. But now I wonder how to check the no-digit condition at the same time in the same sub-string?

I am trying to keep it neat and simple (as I said I have only just started writing programs longer than a few lines!) so I thought doing a nested loop to check every character against !char.IsNumber(letter) might not be optimal. Or should I first check to see if there is no digit, then see if there is at least a capital character?

I feel confused how to achieve both restrictions, so I would appreciate some help in resolving this issue. I would also appreciate any observations or suggestions you might have. For example, is it OK to store my sub-strings in a list? Should I make a dictionary of some sort? Is my all-possible-sub-string extraction nested-loop optimal?

p.s. Some bits are still unfinished; for example I am still to implement the last step to find the longest sub-string and display to the user its length...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PasswordRestriction
{
    class Program   /// Write a program that, given a string, finds the longest substring that is a valid password and returns its length.
    {
        static void Main(string[] args)
        {
            // Ask the user for an alphanumeric string.
            Console.WriteLine("Enter a string of alphanumeric values:");

            // Receive the user input as string.
            string password = Console.ReadLine();

            // Print the length of the longest qualifying substring of the user string.
            Console.WriteLine("Length of the longest qualifying substring:\n" + Solution(password).Length );

            // Prevent the console window from closing.
            Console.ReadLine();
        }


        /// The method that exracts the longest substring that is a valid password.
        /// Note that a substring is a 'contiguous' segment of a string.
        public static string Solution(string str)
        {
            // Only allow non-empty strings.
            if ( String.IsNullOrEmpty(str) )
            {
                return "";
            }
            else
            {
                // Only allow letters and digits.
                if ( str.All(char.IsLetterOrDigit) )
                {
                    // A list for containing qualifying substrings.
                    List<string> passwordList = new List<string>();

                    // Generate all possible substrings. Note that
                    // string itself is not a substring of itself!
                    for (int i = 1; i < str.Length; i++)
                    {
                        for (int j = 0; j <= (str.Length-i); j++)
                        {
                            string subStr = str.Substring(j, i);
                            Console.WriteLine(subStr);

                            bool containsNum = false;
                            bool containsUpper = false;

                            // Convert the substrings to arrays of characters with ToCharArray.
                            // This method is called on a string and returns a new character array.
                            // You can manipulate this array in-place, which you cannot do with a string.
                            char[] subStrArray = subStr.ToCharArray();

                            // Go through every character in each substring.
                            // If there is at least one uppercase letter and
                            // no digits, put the qualifying substring in a list.
                            for (int k = 0; k < subStrArray.Length; k++)
                            {
                                char letter = subStrArray[k];

                                if ( char.IsNumber(letter) )
                                {
                                    containsNum = true;
                                    break;
                                }

                                if ( char.IsUpper(letter) )
                                {
                                    containsUpper = true;
                                }

                                if ( containsUpper && (containsNum == false) && (k == subStrArray.Length - 1) )
                                {
                                    Console.WriteLine("Found the above legit password!");
                                    passwordList.Add(subStr);
                                }
                            }
                        }
                    }

                    //Find the longest stored string in the list.
                    //if (passwordList.Count != 0)
                    //{
                        string maxLength = passwordList[0];

                        foreach (string s in passwordList)
                        {
                            if (s.Length > maxLength.Length)
                            {
                                maxLength = s;
                            }
                        }
                    //}

                    // Return the qualifying substring.
                    return maxLength;
                }
                else
                {
                    return "aaaaaaaaaa";
                }
            }
        }
    }
}

Upvotes: 2

Views: 313

Answers (2)

VortixDev
VortixDev

Reputation: 1013

As an alternative to the Linq answer, and if I understand you correctly, this is what I'd do, replacing the content of the str.All condition:

string qualifier;
string tempQualifier;
bool containsUpper = false;
for (int i = 0; i < str.Length(); i++) {
    tempQualifier += str[i];
    if (char.IsNumber(str[i])) {
        if (containsUpper) {
            if (tempQualifier.Length > qualifier.Length && tempQualifier.Length != str.Length) {
                qualifier = tempQualifier;
            }

            containsUpper = false;
        }

        tempQualifier = "";
    } else if (char.IsUpper(str[i])) {
        containsUpper = true;
    }
}

return qualifier;

This would go through the string, building up the substring until it comes across a number. If the substring contains an uppercase letter and is longer than any previous qualifier, it is stored as the new qualifier (also assuming that it isn't the length of the string provided). Apologies if I've made any mistakes (I'm not well versed in C#).

It's much longer than the Linq answer, but I thought it'd be handy for you to see the process broken down so you can understand it better.

Upvotes: 0

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186678

A good problem for Linq

  • contains no digits - Split on digits
  • at least one upper-case letter - Where + Any
  • longest (not shortest) OrderByDescending
  • longest (just one) - FirstOrDefault

Implementation

string source = ....

var result = source
  .Split('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
  .Where(line => line.Any(c => c >= 'A' && c <= 'Z')) // or char.IsUpper(c)
  .OrderByDescending(line => line.Length)
  .FirstOrDefault(); // null if there're no such substrings at all

Upvotes: 3

Related Questions