Complex palindrome words - c#

I have a method who checks if it is a palindrome or not
public bool IsPalindrome(PalindromeModel model)
{
// First, check whether input string is null or whitespace.
// If yes, then return false.
if (string.IsNullOrWhiteSpace(model.Value))
return false;
var inputDict = new Dictionary<char, int>();
// Big/small letter is not important
var lowerInputStr = model.Value.ToLower();
// Fill input dictionary
// If hit a space, then skip it
for (var i = 0; i < lowerInputStr.Length; i++)
{
if (lowerInputStr[i] != ' ')
{
if (inputDict.ContainsKey(lowerInputStr[i]))
inputDict[lowerInputStr[i]] += 1;
else
inputDict.Add(lowerInputStr[i], 1);
}
}
var countOdds = 0;
foreach (var elem in inputDict)
{
if (elem.Value % 2 != 0)
countOdds++;
}
return countOdds <= 1;
}
So it works with this words: dood, A Santa Lived As a Devil At NASA
But for more complex palindromes like Mr. Owl Ate My Metal Worm it returns false, but it should be true, how can I achieve this?

Remove the spaces and the punctuations before the first loop
var lowerInputStr = new string(model.Value
.ToCharArray()
.Where(c => !char.IsPunctuation(c)).ToArray())
.ToLower()
.Replace(" ", "");

That solution looks overly complex. I'd just walk in from both edges, skipping over any characters you don't define as being in scope for comparison. In this implementation, I only consider letters while ignoring case. Note that the solution doesn't allocate any additional memory (as for example string.Replace() or string.ToLower() do internally) and runs worst case input (it is a palendrome) in O(N).
bool IsPalendrome(string input)
{
var left = 0;
var right = input.Length - 1;
while (left < right)
{
left = SkipNonLetters(input, left, 1);
right = SkipNonLetters(input, right, -1);
if (char.ToUpperInvariant(input[left]) != char.ToUpperInvariant(input[right]))
return false;
left++;
right--;
}
return true;
}
int SkipNonLetters(string input, int startAt, int direction)
{
while (startAt >= 0 && startAt < input.Length && !char.IsLetter(input[startAt]))
startAt += direction;
return startAt;
}
Examples:
Console.WriteLine(IsPalendrome("dood"));
Console.WriteLine(IsPalendrome("A Santa Lived As a Devil At NASA"));
Console.WriteLine(IsPalendrome("Mr. Owl Ate My Metal Worm"));
Output:
True
True
True

To expand on what Muhammad Sulaiman said, after you have the string cleaned up, the rest of the method can be a lot simpler
var lowerInputStr = new string(input.ToCharArray()
.Where(c => !char.IsPunctuation(c))
.ToArray())
.ToLower()
.Replace(" ", "");
return lowerInputStr.Substring(lowerInputStr.Length / 2) == (new String(lowerInputStr.Reverse().ToArray()))
.Substring(lowerInputStr.Length / 2);

Related

Return index of first difference between strings [duplicate]

This question already has answers here:
Comparing strings and get the first place where they vary from eachother
(8 answers)
Closed 4 years ago.
I am wondering if there is a fancy way to find the index of, the first character in string a that does not match the character in the same position of string b, aside from using brute force.
Here is the brute force attempt:
bool OnlyDiffersByCarotsAndSpaces(string a, string b)
{
if( a.Count() != b.Count() )
{
return false;
}
for(int index = 0; index < a.Count(); ++index)
{
if( a[index] != b[index] )
{
string validCharacters = " ^";
if( !validCharacters.Contains(a[index]) ||
!validCharacters.Contains(b[index]) )
{
return false;
}
}
}
return true;
}
I think you can use a combination of string.Split to break up your strings into arrays, split on the "valid characters", and then return the result of IEnumerable.SequenceEqual, which returns true if two IEnumerables contain the same elements in the same order:
private static bool OnlyDiffersByCarotsAndSpaces(string a, string b)
{
// A few "exit quick" checks...
if (a == null) return b == null;
if (b == null) return false;
var validChars = new[] {' ', '^'};
var first = a.Split(validChars);
var second = b.Split(validChars);
return first.SequenceEqual(second);
}
int DifferenceIndex(string str1, string str2)
{
int position = 0;
int min = Math.Min(str1.Length, str2.Length);
while (position < min && str1[position] == str2[position])
position ++;
return (position == min && str1.Length == str2.Length) ? -1 : position;
}
Returns -1 if the strings are the same.
Did somebody say fancy? :) This version will definitely perform worse than your version for high traffic and large strings, so depending on performance requirements you might not want to use it.
if (a.Length != b.Length) return -1;
string allowed = " ^";
a = a.Replace(' ', '^');
b = b.Replace(' ', '^');
//at this point you can do string.Equals(a,b) and it will work correctly
var equalCharCount = a.Zip(b, (c1, c2) => c1 == c2).TakeWhile(c => c).Count();
//or even
//var idx = 0;
//var equalCharCont = a.TakeWhile(c => c == b[idx++]).Count() + 1;
A slightly fancier, worse performing, unfinished and most probably wrong idea would be:
var idx = a.Zip(b, (i1, i2) => Tuple.Create(i1, i2))
.Select((value, index) => new { value, index })
.SkipWhile(it => it.value.Item1 == it.value.Item2 ||
(allowed.Contains(it.value.Item1) &&
allowed.Contains(it.value.Item2)))
.Select(it => it.index)
.First() + 1;
Well, that depends on what you mean by "fancy", and whether or not that's really what you want. For little utilities like this, it's often better to be as fast and make as little fuss as possible. Solutions involving IEnumerable will almost certainly be a bit more readable and "elegant", but under the covers, they'll accomplish the same thing, but likely with more overhead.
This method is slightly "brutier" and "forcier" than the original.
bool OnlyDiffersByCarotsAndSpaces(string a, string b)
{
int len = a.Length; // eliminate Count() function call
if (len != b.Length)
{
return false;
}
for (int index = 0; index < len; ++index)
{
if ((a[index] != b[index]) &&
(((a[index] != ' ') && (a[index] != '^')) || // eliminate Contains() method calls
((b[index] != ' ') && (b[index] != '^')))) //
{
return false;
}
}
return true;
}

Use Regex to remove outer parentheses from nested expression, but leave inner parentheses?

I'm working on figuring out a good regular expression that would take a value such as:
Transformer Winding Connections (Wye (Star) or Delta)
and would match:
Wye (Star) or Delta
What I have so far is:
string longName = "Transformer Winding Connections (Wye (Star) or Delta)";
// Match everything until first parentheses
Regex nameRegex = new Regex(#"([^(]*)");
Match nameMatch = nameRegex.Match(longName);
// Match everything from first parentheses on
Regex valueRegex = new Regex(#"\(.+\)");
Match valueMatch = valueRegex.Match(longName);
valueMatch is returning:
(Wye (Star) or Delta)
Is there some clever way to only remove the first set of parentheses in C#?
If you want to deal with only one level then this would be fine.
#"\((?:\([^()]*\)|[^()])*\)"
or
If you don't want to match the outer paranthesis.
#"(?<=\()(?:\([^()]*\)|[^()])*(?=\))"
DEMO
Here's the non-regex solution I mentioned in a comment, assuming your scenario is as simple as you laid out:
string longName = "Transformer Winding Connections (Wye (Star) or Delta)";
int openParenIndex = longName.IndexOf("(");
int closingParenIndex = longName.LastIndexOf(")");
if (openParenIndex == -1 || closingParenIndex == -1
|| closingParenIndex < openParenIndex)
{
// unexpected scenario...
}
string valueWithinFirstLastParens = longName.Substring(openParenIndex + 1,
closingParenIndex - openParenIndex - 1);
Try this function, which doesn't use RegEx:
private static string RemoveOuterParenthesis(string str)
{
int ndx = 0;
int firstParenthesis = str.IndexOf("(", StringComparison.Ordinal);
int balance = 1;
int lastParenthesis = 0;
while (ndx < str.Length)
{
if (ndx == firstParenthesis)
{
ndx++;
continue;
}
if (str[ndx] == '(')
balance++;
if (str[ndx] == ')')
balance--;
if (balance == 0)
{
lastParenthesis = ndx;
break;
}
ndx++;
}
return str.Remove(firstParenthesis, 1).Remove(lastParenthesis - 1, 1);
}
You'll want to clean it up a bit. Do some error checking. The functions assumes:
The string has parenthesis
The parenthesis are balanced
The string isn't null

Check if string is anagram of palindrome

My question is:
How do you check if a given string is the anagram of a palindrome?
I found some solutions in Python on the internet, but I'm not sure how can I check this yet. I was thinking about converting the strig to a char [], and then get the HashCode for every character, but I'm stuck.
If you're not interested in the palindrome or anagram being a real word then I think you can reframe the problem as check if a given string has no more than one character that appears an uneven number of times. This is on the basis that only the middle character can possibly occur an odd number of times. As long as that is satisfied then you can form a palindrome from the string.
To do that you can use Linq. Something like this perhaps:
private static bool IsPalindromeAnagram(string test)
{
var charCount = test.GroupBy(c => c, (c, i) => new
{
character = c,
count = i.Count()
});
return charCount.Count(c => c.count % 2 == 1) <= 1;
}
Saved char in a dictionary as key, then check if more than one key is odd. this way also have all unique char ready to make anagram.
var length = s.Length;
if (length == 0) return false;
var dic = new Dictionary<char, int>();
for (var i = 0; i < length; i++)
{
if (dic.ContainsKey(s[i]))
{
dic[s[i]]++;
continue;
}
dic.Add(s[i], 1);
}
int odd = 0;
foreach (var pv in dic)
{
if (odd > 1) return false;
if (pv.Value % 2 == 0)
{
continue;
}
odd++;
}

What is the most efficient way to detect if a string contains a number of consecutive duplicate characters in C#?

For example, a user entered "I love this post!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
the consecutive duplicate exclamation mark "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" should be detected.
The following regular expression would detect repeating chars. You could up the number or limit this to specific characters to make it more robust.
int threshold = 3;
string stringToMatch = "thisstringrepeatsss";
string pattern = "(\\d)\\" + threshold + " + ";
Regex r = new Regex(pattern);
Match m = r.Match(stringToMatch);
while(m.Success)
{
Console.WriteLine("character passes threshold " + m.ToString());
m = m.NextMatch();
}
Here's and example of a function that searches for a sequence of consecutive chars of a specified length and also ignores white space characters:
public static bool HasConsecutiveChars(string source, int sequenceLength)
{
if (string.IsNullOrEmpty(source))
return false;
if (source.Length == 1)
return false;
int charCount = 1;
for (int i = 0; i < source.Length - 1; i++)
{
char c = source[i];
if (Char.IsWhiteSpace(c))
continue;
if (c == source[i+1])
{
charCount++;
if (charCount >= sequenceLength)
return true;
}
else
charCount = 1;
}
return false;
}
Edit fixed range bug :/
Can be done in O(n) easily: for each character, if the previous character is the same as the current, increment a temporary count. If it's different, reset your temporary count. At each step, update your global if needed.
For abbccc you get:
a => temp = 1, global = 1
b => temp = 1, global = 1
b => temp = 2, global = 2
c => temp = 1, global = 2
c => temp = 2, global = 2
c => temp = 3, global = 3
=> c appears three times. Extend it to get the position, then you should be able to print the "ccc" substring.
You can extend this to give you the starting position fairly easily, I'll leave that to you.
Here is a quick solution I crafted with some extra duplicates thrown in for good measure. As others pointed out in the comments, some duplicates are going to be completely legitimate, so you may want to narrow your criteria to punctuation instead of mere characters.
string input = "I loove this post!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!aa";
int index = -1;
int count =1;
List<string> dupes = new List<string>();
for (int i = 0; i < input.Length-1; i++)
{
if (input[i] == input[i + 1])
{
if (index == -1)
index = i;
count++;
}
else if (index > -1)
{
dupes.Add(input.Substring(index, count));
index = -1;
count = 1;
}
}
if (index > -1)
{
dupes.Add(input.Substring(index, count));
}
The better way i my opinion is create a array, each element in array is responsible for one character pair on string next to each other, eg first aa, bb, cc, dd. This array construct with 0 on each element.
Solve of this problem is a for on this string and update array values.
You can next analyze this array for what you want.
Example: For string: bbaaaccccdab, your result array would be { 2, 1, 3 }, because 'aa' can find 2 times, 'bb' can find one time (at start of string), 'cc' can find three times.
Why 'cc' three times? Because 'cc'cc & c'cc'c & cc'cc'.
Use LINQ! (For everything, not just this)
string test = "aabb";
return test.Where((item, index) => index > 0 && item.Equals(test.ElementAt(index)));
// returns "abb", where each of these items has the previous letter before it
OR
string test = "aabb";
return test.Where((item, index) => index > 0 && item.Equals(test.ElementAt(index))).Any();
// returns true

How to make next step of a string. C#

The question is complicated but I will explain it in details.
The goal is to make a function which will return next "step" of the given string.
For example
String.Step("a"); // = "b"
String.Step("b"); // = "c"
String.Step("g"); // = "h"
String.Step("z"); // = "A"
String.Step("A"); // = "B"
String.Step("B"); // = "C"
String.Step("G"); // = "H"
Until here its quite easy, But taking in mind that input IS string it can contain more than 1 characters and the function must behave like this.
String.Step("Z"); // = "aa";
String.Step("aa"); // = "ab";
String.Step("ag"); // = "ah";
String.Step("az"); // = "aA";
String.Step("aA"); // = "aB";
String.Step("aZ"); // = "ba";
String.Step("ZZ"); // = "aaa";
and so on...
This doesn't exactly need to extend the base String class.
I tried to work it out by each characters ASCII values but got stuck with strings containing 2 characters.
I would really appreciate if someone can provide full code of the function.
Thanks in advance.
EDIT
*I'm sorry I forgot to mention earlier that the function "reparse" the self generated string when its length reaches n.
continuation of this function will be smth like this. for example n = 3
String.Step("aaa"); // = "aab";
String.Step("aaZ"); // = "aba";
String.Step("aba"); // = "abb";
String.Step("abb"); // = "abc";
String.Step("abZ"); // = "aca";
.....
String.Step("zzZ"); // = "zAa";
String.Step("zAa"); // = "zAb";
........
I'm sorry I didn't mention it earlier, after reading some answers I realised that the problem was in question.
Without this the function will always produce character "a" n times after the end of the step.
NOTE: This answer is incorrect, as "aa" should follow after "Z"... (see comments below)
Here is an algorithm that might work:
each "string" represents a number to a given base (here: twice the count of letters in the alphabet).
The next step can thus be computed by parsing the "number"-string back into a int, adding 1 and then formatting it back to the base.
Example:
"a" == 1 -> step("a") == step(1) == 1 + 1 == 2 == "b"
Now your problem is reduced to parsing the string as a number to a given base and reformatting it. A quick googling suggests this page: http://everything2.com/title/convert+any+number+to+decimal
How to implement this?
a lookup table for letters to their corresponding number: a=1, b=2, c=3, ... Y = ?, Z = 0
to parse a string to number, read the characters in reverse order, looking up the numbers and adding them up:
"ab" -> 2*BASE^0 + 1*BASE^1
with BASE being the number of "digits" (2 count of letters in alphabet, is that 48?)
EDIT: This link looks even more promising: http://www.citidel.org/bitstream/10117/20/12/convexp.html
Quite collection of approaches, here is mine:-
The Function:
private static string IncrementString(string s)
{
byte[] vals = System.Text.Encoding.ASCII.GetBytes(s);
for (var i = vals.Length - 1; i >= 0; i--)
{
if (vals[i] < 90)
{
vals[i] += 1;
break;
}
if (vals[i] == 90)
{
if (i != 0)
{
vals[i] = 97;
continue;
}
else
{
return new String('a', vals.Length + 1);
}
}
if (vals[i] < 122)
{
vals[i] += 1;
break;
}
vals[i] = 65;
break;
}
return System.Text.Encoding.ASCII.GetString(vals);
}
The Tests
Console.WriteLine(IncrementString("a") == "b");
Console.WriteLine(IncrementString("z") == "A");
Console.WriteLine(IncrementString("Z") == "aa");
Console.WriteLine(IncrementString("aa") == "ab");
Console.WriteLine(IncrementString("az") == "aA");
Console.WriteLine(IncrementString("aZ") == "ba");
Console.WriteLine(IncrementString("zZ") == "Aa");
Console.WriteLine(IncrementString("Za") == "Zb");
Console.WriteLine(IncrementString("ZZ") == "aaa");
public static class StringStep
{
public static string Next(string str)
{
string result = String.Empty;
int index = str.Length - 1;
bool carry;
do
{
result = Increment(str[index--], out carry) + result;
}
while (carry && index >= 0);
if (index >= 0) result = str.Substring(0, index+1) + result;
if (carry) result = "a" + result;
return result;
}
private static char Increment(char value, out bool carry)
{
carry = false;
if (value >= 'a' && value < 'z' || value >= 'A' && value < 'Z')
{
return (char)((int)value + 1);
}
if (value == 'z') return 'A';
if (value == 'Z')
{
carry = true;
return 'a';
}
throw new Exception(String.Format("Invalid character value: {0}", value));
}
}
Split the input string into columns and process each, right-to-left, like you would if it was basic arithmetic. Apply whatever code you've got that works with a single column to each column. When you get a Z, you 'increment' the next-left column using the same algorithm. If there's no next-left column, stick in an 'a'.
I'm sorry the question is stated partly.
I edited the question so that it meets the requirements, without the edit the function would end up with a n times by step by step increasing each word from lowercase a to uppercase z without "re-parsing" it.
Please consider re-reading the question, including the edited part
This is what I came up with. I'm not relying on ASCII int conversion, and am rather using an array of characters. This should do precisely what you're looking for.
public static string Step(this string s)
{
char[] stepChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
char[] str = s.ToCharArray();
int idx = s.Length - 1;
char lastChar = str[idx];
for (int i=0; i<stepChars.Length; i++)
{
if (stepChars[i] == lastChar)
{
if (i == stepChars.Length - 1)
{
str[idx] = stepChars[0];
if (str.Length > 1)
{
string tmp = Step(new string(str.Take(str.Length - 1).ToArray()));
str = (tmp + str[idx]).ToCharArray();
}
else
str = new char[] { stepChars[0], str[idx] };
}
else
str[idx] = stepChars[i + 1];
break;
}
}
return new string(str);
}
This is a special case of a numeral system. It has the base of 52. If you write some parser and output logic you can do any kind of arithmetics an obviously the +1 (++) here.
The digits are "a"-"z" and "A" to "Z" where "a" is zero and "Z" is 51
So you have to write a parser who takes the string and builds an int or long from it. This function is called StringToInt() and is implemented straight forward (transform char to number (0..51) multiply with 52 and take the next char)
And you need the reverse function IntToString which is also implementet straight forward (modulo the int with 52 and transform result to digit, divide the int by 52 and repeat this until int is null)
With this functions you can do stuff like this:
IntToString( StringToInt("ZZ") +1 ) // Will be "aaa"
You need to account for A) the fact that capital letters have a lower decimal value in the Ascii table than lower case ones. B) The table is not continuous A-Z-a-z - there are characters inbetween Z and a.
public static string stepChar(string str)
{
return stepChar(str, str.Length - 1);
}
public static string stepChar(string str, int charPos)
{
return stepChar(Encoding.ASCII.GetBytes(str), charPos);
}
public static string stepChar(byte[] strBytes, int charPos)
{
//Escape case
if (charPos < 0)
{
//just prepend with a and return
return "a" + Encoding.ASCII.GetString(strBytes);
}
else
{
strBytes[charPos]++;
if (strBytes[charPos] == 91)
{
//Z -> a plus increment previous char
strBytes[charPos] = 97;
return stepChar(strBytes, charPos - 1); }
else
{
if (strBytes[charPos] == 123)
{
//z -> A
strBytes[charPos] = 65;
}
return Encoding.ASCII.GetString(strBytes);
}
}
}
You'll probably want some checking in place to ensure that the input string only contains chars A-Za-z
Edit Tidied up code and added new overload to remove redundant byte[] -> string -> byte[] conversion
Proof http://geekcubed.org/random/strIncr.png
This is a lot like how Excel columns would work if they were unbounded. You could change 52 to reference chars.Length for easier modification.
static class AlphaInt {
private static string chars =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static string StepNext(string input) {
return IntToAlpha(AlphaToInt(input) + 1);
}
public static string IntToAlpha(int num) {
if(num-- <= 0) return "a";
if(num % 52 == num) return chars.Substring(num, 1);
return IntToAlpha(num / 52) + IntToAlpha(num % 52 + 1);
}
public static int AlphaToInt(string str) {
int num = 0;
for(int i = 0; i < str.Length; i++) {
num += (chars.IndexOf(str.Substring(i, 1)) + 1)
* (int)Math.Pow(52, str.Length - i - 1);
}
return num;
}
}
LetterToNum should be be a Function that maps "a" to 0 and "Z" to 51.
NumToLetter the inverse.
long x = "aazeiZa".Aggregate((x,y) => (x*52) + LetterToNum(y)) + 1;
string s = "";
do { // assertion: x > 0
var c = x % 52;
s = NumToLetter() + s;
x = (x - c) / 52;
} while (x > 0)
// s now should contain the result

Categories

Resources