C# /Linq Yet another Brain Teaser - c#

Friends, there is yet another scenario to solve. I am working it out without applying Linq.But I hope it is good opportunity for me to learn Linq if you share your code in Linq.
It is know as FLAMES
F - Friend
L - Lover
A - Admirer
M - Marry(Husband)
E - Enemy
S - Sister
Problem description:
Two names will be given (male, female).We have to strike out the common letters from both names. Then we have to count the number of remaining letters after striking out the common characters from both names. Finally we have to iterate the string FLAMES and striking out the letters in FLAMES until we will reach single character left. The remaining single character shows the relationship. I will explain the process more details in the following example.(Ignore cases and spaces).
Example :
Step 1
Male : Albert
Female : Hebarna
Letters “a”, “e” ,”b” are common in both names.
( Strike those letters from both string , even the name “Hebarna” contains two “a” you are allowed to strike single “a” from both string because The name “Albert” has only single “a”).
The resultant string is
Male : $ l $ $ r t
Female: H $ $ $ r n a
Step 2:
Count the remaining letters from both strings.
Count : 7
Step 3:
Using the count we have to iterate the string “FLAMES” in the following manner
F L A M E S
1 2 3 4 5 6
7
(Here the count 7 ends at F ,so strike F)
you will get
$ L A M E S
(Again start your count from immediate next letter (it should not already be hit out) if it is the last letter (“S”) then start from first letter “F” if ‘F” is not already hit out.
$ L A M E S
(igonre) 1 2 3 4 5
(ignore) 6 7
During counting never consider hit out letters.
$ L $ M E S
1 2 3
ignore 4 ignore 5 6 7
"s" will be hit out.
$ L $ M E $
ignore 1 ignore 2 3
4 ignore 5 6
7
"L" will be hit out
$ $ $ M E $
ignore 1 2 ignore
ignore ignore ignore 3 4 ignore
5 6
7
Finally "M" will be hit out. Then only remaining letter is "E" So albert is enemy to herbana.
Update :
Lettter "r" is also common in both names.I forgor to hit it out.Anyhow the process is same as explained.Thanks for pointing it out.

Step1 and Step2
var firstLookup = firstName.ToLookup(c => c.ToLower());
var secondLookup = secondName.ToLookup(c => c.ToLower());
var allChars = firstLookup.Keys.Union(secondLookup.Keys);
int count =
(
from c in allChars
let firstCount = firstLookup[c].Count()
let secondCount = secondLookup[c].Count()
select
firstCount < secondCount ? secondCount - firstCount :
firstCount - secondCount
).Sum()
Step3 (untested)
List<char> word = "FLAMES".ToList();
while (word.Count > 1)
{
int wordCount = word.Count;
int remove = (count-1) % wordCount;
word =
word.Select( (c, i) => new {c, i =
i == remove ? 0 :
i < remove ? i + wordCount + 1 :
i})
.OrderBy(x => x.i)
.Select(x => x.c)
.Skip(1)
.ToList();
}
char result = word.Single();

var count = male.Length + female.Length - male.Intersect( female ).Count();
while (flames.Length > 1)
{
flames = string.Join( '', flames.Where( (c,i) => i != (count % flames.Length) -1 ).ToArray() );
}

That last calculation part that iterates through the flames-letters and removing one after another can be precalculated.
public static Char GetChar(int diff) {
var idx = (diff - 1) % 60;
return "efefmeaelmaafmfaflefefeemsasamfmfallslslesmsasmmaelmlaslslfs"[idx];
}
Some things can be done without linq... unless I totally messed something up.

Related

Finding the length of sub-string using regular expression

I have managed to match the groups as follows using the expression,
\([^\)]*\)
Example strings are,
s11(h 1 1 c)(h 1 1 c) x="" y="" z="" phi="" theta=""
e(45,10,h 1 1 c,1,cross,max) x="" y="" z="" phi="" theta=""
With the above expression I can match (h 1 1 c)(h 1 1 c) and (45,10,h 1 1 c,1,cross,max)
But I want to capture the starting string s11 and e along with (h 1 1 c)(h 1 1 c) and (45,10,h 1 1 c,1,cross,max)
Asking for length until first occurence is the same as asking of index of first occurence.
No need for regular expressions here, as stated in comments, method returning index of first occurence will suffice:
var lengthOfSubstring = "rxx(a 0 0 b)(a 0 0 b)".IndexOf(')'); // returns 11
if( lengthOfSubstring < 0 )
{
// character not found within the string, so the substring doesn't exist
}

Regex - 1st group 1 time, 2nd group Multiple times

I have data like -
06deepaksharma
i need regex to split the data as
06 > then multiple group of (06 char)
so its going to be like
first 2 digit then multiple groups, each with the length of first 2 digit value.
01DE > 01 D E 01 - then 2 group each 1 char length
02DE > 02 DE 02 - then 1 group each 2 char length
02DESH > 02 DE SH 02 - then 2 group each 2 char length
03DEESHA > 03 DEE SHA 03 - then 2 group each 3 char length
01DEESHA > 01 D E E S H A 01 - then 6 group each 1 char length
Hope now its clear what i want.
I am not getting how to fix the length for second group on the basis of first group value and how to define that second group may occur N times.
UPDATE BELOW ---
so if we can not apply the length on second group then can we get all the possibility if I say i fix the length of second group?
mean if length going to be 2 for char groups
01DE > 01 DE
01DEEPAK > 01 DE EP AK
XXDEEP > XX DE EP
So if we say length going to be 2 all the times, now can be get the desired result as stated in UPDATED parts
You can achieve what you described in the beginning of your question with both regex and LINQ:
var input = "03DEESHA";
var result = new List<string>();
var mtch = Regex.Match(input, #"^(\d+)(.*)"); // Get the Match object with captured texts
result.Add(mtch.Groups[1].Value); // Add the number to the resulting list
var chunks = Regex.Matches(mtch.Groups[2].Value, // Get all chunks
string.Format(".{{{0}}}", int.Parse(mtch.Groups[1].Value)))
.Cast<Match>()
.Select(p => p.Value)
.ToList();
result.AddRange(chunks);
The regex ^(\d+)(.*) matches any numbers in the beginning (Group 1), and then captures the rest of a single-line string (with no newlines, if you want to support them, add a RegexOptions.Singleline flag to the Regex.Match) into Group 2.
Result of the above code execution:
If you have strings where the number of the letters cannot be divided by the initial number without a remainder, instead of ".{{{0}}}" use ".{{1,{0}}}".
I don´t think you can use regex here as you need to use a back-ref with variable value.
However you may consider a simple linq on the characters:
// first get the number of characters to read
int num = Convert.ToInt32(myString.Substring(0, 2));
// now a simple loop on the characters
for(int i = 2; i < myString.Length; i += num) result.Add(myString.SubString(i, num);
Or if you really want a regex parse the number first and THEN apply your regex:
var r = "([a-zA-Z]{" + num + "})";
var res = new Regex(r).Split(new string(myString.Skip(2).ToArray()));

LINQ non-linear order by string length

I'm trying to get a list of string ordered such that the longest are on either end of the list and the shortest are in the middle. For example:
A
BB
CCC
DDDD
EEEEE
FFFFFF
would get sorted as:
FFFFFF
DDDD
BB
A
CCC
EEEEE
EDIT: To clarify, I was specifically looking for a LINQ implementation to achieve the desired results because I wasn't sure how/if it was possible to do using LINQ.
You could create two ordered groups, then order the first group descending(already done) and the second group ascending:
var strings = new List<string> {
"A",
"BB",
"CCC",
"DDDD",
"EEEEE",
"FFFFFF"};
var two = strings.OrderByDescending(str => str.Length)
.Select((str, index) => new { str, index })
.GroupBy(x => x.index % 2)
.ToList(); // two groups, ToList to prevent double execution in following query
List<string> ordered = two.First()
.Concat(two.Last().OrderBy(x => x.str.Length))
.Select(x => x.str)
.ToList();
Result:
[0] "FFFFFF" string
[1] "DDDD" string
[2] "BB" string
[3] "A" string
[4] "CCC" string
[5] "EEEEE" string
Don't ask how and why... ^^
list.Sort(); // In case the list is not already sorted.
var length = list.Count;
var result = Enumerable.Range(0, length)
.Select(i => length - 1 - 2 * i)
.Select(i => list[Math.Abs(i - (i >> 31))])
.ToList();
Okay, before I forget how it works, here you go.
A list with 6 items for example has to be reordered to this; the longest string is at index 5, the shortest one at index 0 of the presorted list.
5 3 1 0 2 4
We start with Enumerable.Range(0, length) yielding
0 1 2 3 4 5
then we apply i => length - 1 - 2 * i yielding
5 3 1 -1 -3 -5
and we have the non-negative part correct. Now note that i >> 31 is an arithmetic left shift and will copy the sign bit into all bits. Therefore non-negative numbers yield 0 while negative numbers yield -1. That in turn means subtracting i >> 31 will not change non-negative numbers but add 1 to negative numbers yielding
5 3 1 0 -2 -4
and now we finally apply Math.Abs() and get
5 3 1 0 2 4
which is the desired result. It works similarly for lists of odd length.
Just another option, which I find more readable and easy to follow:
You have an ordered list:
var strings = new List<string> {
"A",
"BB",
"CCC",
"DDDD",
"EEEEE",
"FFFFFF"};
Create a new list and simply alternate where you add items::
var new_list = new List<string>(); // This will hold your results
bool start = true; // Insert at head or tail
foreach (var s in strings)
{
if (start)
new_list.Insert(0,s);
else
new_list.Add(s);
start = !start; // Flip the insert location
}
Sweet and simple :)
As for Daniel Bruckner comment, if you care about which strings comes first, you could also change the start condition to:
// This will make sure the longest strings is first
bool start= strings.Count()%2 == 1;

Issue with LINQ

I created LINQ implementation of mod10 algorithm.
The source code:
string number = "7992739871";
int mod10sum = number.Reverse()
.Select((c, i) => (c - '0') << ((i + 1) & 1)) // Double every other digit and sum the digits of the products (e.g., 10: 1 + 0 = 1, 14: 1 + 4 = 5)
.Sum(c => c - '0') % 10; // together with the undoubled digits from the original number
string checkDigit = (mod10sum == 0 ? 0 : 10 - mod10sum).ToString("0");
Console.WriteLine(checkDigit);
As per the example, 7992739871 number should have check digit as 3; however, what I am getting is 15.
What I am doing wrong? I am sure the mistake is very small but can't find it.
The problem is with the Select method. In order to sum up all the digits (as described in the algorithm) you would need to return 1 and 0 instead of 10, 1 and 4 instead of 14 (as in your example).
The easiest (but it does not have to be the most optimal) way to do that is to conert number from Select to string (14 -> "14") and then split the string characters using SelectMany.
So your code should look as follows:
int mod10sum = number.Reverse()
.SelectMany((c, i) => ((c - '0') << ((i + 1) & 1)).ToString())
.Sum(c => c - '0') % 10;
checkDigit = (mod10sum == 0 ? 0 : 10 - mod10sum).ToString("0");
Console.WriteLine(checkDigit);
A bit of theory
LINQ SelectMany returns IEnumerable<>. When you return string (which is IEnumerable) then that's why SelectMany "splits" returned string into characters.
Microsoft has very nice page (101 LINQ Samples) with different LINQ samples which should help you out.
EDIT
I would also recommend working on that conversion from int to string. I was working on similar project literally yesterday and in my case that conversion is a bit problematic from performance point of view as we call that method millions of times. If you have to calculate lots of mod10's then it might be not the best solution.
I would change the Sum.
At this point, you don't have a sequence of characters, but the single-or-doubled-as-appropriate value for each original digit.
Thus, you don't need to be subtracting 0, you need to be calculating the digit sum of each of these integers, and (since you know they'll be small) you can do this as simply as
.Sum(i => (i % 10) + (i / 10))
giving
string number = "7992739871";
int mod10sum = number.Reverse()
.Select((c, i) => (c - '0') << ((i + 1) & 1))
.Sum(i => (i % 10) + (i / 10)) % 10;
This should be more effecient than calling ToString() and iterating over the result.

How can I ignore / skip 'n' elements while sorting string in a lexicographical order

I have the following c# code that sorts a string in a lexicographical (alphabetical) order.
string str = "ACGGACGAACT";
IEnumerable<string> sortedSubstrings =
Enumerable.Range(0, str.Length)
.Select(i => str.Substring(i))
.OrderBy(s => s);
Result:
0 AACT
1 ACGAACT
2 ACGGACGAACT
3 ACT
4 CGAACT
5 CGGACGAACT
6 CT
7 GAACT
8 GACGAACT
9 GACGAACT
10 T
However I want to enhance this sort by skipping the 3rd and the 4th character during the lexicographical sort process
In this case the lexicographical sort will be different to the one above.
result:
0 AA[CT
1 AC[T
2 AC[GG]ACGAACT
3 AC[GA]ACT
4 CG[GA]CGAACT
5 CG[AA]CT
6 CT
7 GA[CG]AACT
8 GA[AC]T
9 GG[AC]GAACT
10 T
how can I achieve this?
This can be done by tweaking the lambda passed to OrderBy. Something like this should do it:
var sortedSubstrings =
Enumerable.Range(0, str.Length)
.Select(i => str.Substring(i))
.OrderBy(s => s.Length < 3 ? s : s.Remove(2, Math.Min(s.Length - 2, 2)));
Edit: Corrected off-by-one error.
You can change the lambda passed to OrderBy to one which will remove the 3rd and 4th symbols from the string.
var sorted = source.OrderBy(s => new string(s.Where((ch, n) => n != 2 && n != 3).ToArray()));

Categories