Here's a solution in C# that seems to work from a handful of test cases that I threw at it. [Parts of the regex expression were borrowed from a previous answerer.]
public static bool IsWordNiceable(string word, int maxVowels, int maxConsonants)
{
if (IsUgly(word, maxVowels, maxConsonants)) return false;
int i = 0;
while ((i = word.IndexOf('?', i)) != -1)
{
string newWord = word.Substring(0, i) + "a" + word.Substring(i+1);
bool vowelMakesNice = IsWordNiceable(newWord, maxVowels, maxConsonants);
newWord = word.Substring(0, i) + "b" + word.Substring(i + 1);
bool consonantMakesNice = IsWordNiceable(newWord, maxVowels, maxConsonants);
if (!(vowelMakesNice || consonantMakesNice)) return false;
i++;
}
return true;
}
private static bool IsUgly(string word, int maxVowels, int maxConsonants)
{
string consonants = "bcdfghjklmnpqrstvwxyz";
string vowels = "aeiou";
string uglyRegex = string.Format("([{0}]{{{1},{1}}})|([{2}]{{{3},{3}}})", vowels, maxVowels, consonants, maxConsonants);
Match match = Regex.Match(word.ToLower(), uglyRegex);
return match.Success;
}
This first tests the given word as-is to see if it's ugly (including question marks, if any). Then it loops through the word, replacing the first "?" with both a vowel and a consonant, and calls itself using the new word. If both the vowel and consonant replacement fail to make it nice, then that branch returns false (ugly).