How do combine a dictionary with a function? - c#

I want to set each letter of the alphabet to a numeric value: a = 1, b = 2, c = 3, etc.
I created a dictionary to set each letter to a value. I created a function that takes in a word and finds the total value of its letters. How do I access my created dictionary within my function?
Console.WriteLine("YOUR NAME VALUE IS: " + hashFunction("b"));//I want the output to be 2 (b = 2) in this case.
int hashFunction(string name)
{
int totalSum = 0;
Char [] characters = name.ToCharArray();
for (int letterIndex = 0; letterIndex < characters.Length; letterIndex++)
{
totalSum += Convert.ToInt32(characters[letterIndex]);
}
return totalSum;
}
Dictionary<char, int> charValues = new Dictionary<char, int>();
charValues.Add('a', 1);
charValues.Add('b', 2);
charValues.Add('c', 3);
charValues.Add('d', 4);
charValues.Add('e', 5);
//etc etc etc
}

Since letters a..z within ASCII table are consequent we can compute letter index as
letter - 'a' + 1
without elaborated mappig based on dictionary. Then we can use Linq to find the Sum:
using System.Linq;
...
// static : we don't want "this" within the method
static int hashFunction(string name) => name
.Where(c => c >= 'a' && c <= 'z')
.Sum(c => c - 'a' + 1);
Edit: if you insist on dictionary based solution, you can put it like this:
using System.Linq;
...
static IReadOnlyDictionary<char, int> charValues = Enumerable
.Range('a', 'z' - 'a' + 1)
.ToDictionary(letter => (char)letter, letter - 'a' + 1);
int hashFunction(string name)
{
int totalSum = 0;
foreach (char c in name)
if (charValues.TryGetValue(c, out int v))
totalSum += v;
return totalSum;
}

Why do you need a dictionary?
IEnumerable<int> MapCharsToInts( IEnumerable<char> chars )
{
foreach ( char ch in chars.Map( c => Char.ToLower(c) )
{
int v = 1 + ch - 'a' ;
yield v >= 1 && v <= 26 ? v : 0 ;
}
}

How do I access my created dictionary within my function?
by the name of the variable. You use the [ ] brakets like in an array, but you pass a key values as parameter into it. In your case one of the chars that you retrieve from your char array. You can actually use the string directly to access the chars, since it is a char array under the hood:
int hashFunction(string name)
{
int totalSum = 0;
for (int letterIndex = 0; letterIndex < name.Length; letterIndex++)
{
char theKey = name[letterIndex];
totalSum += charValues[theKey];
}
return totalSum;
}
the dictionary will then return the appropriate integer, or it will throw an exception if it does not find any match.
The most important part is that the dictionary has to be declared as field or property on class level! not a local variable in some other method.

Related

IndexOf n-th character of that type

I need to get the index of the n-th character of that type in a string.
Example:
string test = "asdfasdfasdf";
int index = test.IndexOf("a", 3);
and index would be 8, since the 3rd a has the index of 8
Is there a function that does that or do you have any idea how to do that in a smart way?
You can do it in a single line, but it is not pretty:
var s = "aabbabhjhjdsfbaxt";
var idx = s.Select((c, i) => new {c, i})
.Where(p => p.c == 'a')
.Skip(2)
.FirstOrDefault()?.i ?? -1;
The idea is to pair up characters with their indexes, filter by character, skip n-1 items, and take the next one if it exists.
Another approach would be to use regex with look-behind:
var idx = Regex.Match(s, "(?<=(a[^a]*){2})a").Index;
This matches an 'a' preceded by two more 'a's, possibly with other characters in the middle.
A simple for loop will do the trick, but you can place the for loop in an extension method to get this functionality as a one-liner throughout your application.
Like this:
public static int NthIndexOf(this string text, char letter, int occurrence)
{
if (text == null) throw new ArgumentNullException(nameof(text));
int count = 0;
for (int index = 0; index < text.Length; index++)
if (text[index] == letter)
{
if (++count == occurrence)
return index;
}
return -1;
}
Usage:
string test = "asdfasdfasdf";
int index = test.IndexOf('a', 3);
You can create an extension method for this like that:
public static int NthIndexOf(this string s, char c, int occurence)
{
if (s == null) throw new ArgumentNullException(nameof(s));
if (occurence <= 0) return -1;
int i = -1, o = 0;
do
{
i = s.IndexOf(c, i+1);
o++;
} while (i >= 0 && o < occurence);
return i;
}
After checking the input arguments it consecutivly uses string.IndexOf() to get the n-th index of the desired character c.
If no further occurence of c is found (IndexOf returns -1) the loop breaks.
Usage:
string test = "asdfasdfasdf";
int index = test.NthIndexOf('a', 3); // 8
Of course you can do the same with a parameter of type string for c instead of char, but would need to change s.IndexOf(c, i+1) to s.IndexOf(c, i+c.Length).
As René Vogt said you should use an extension method chaining calls to IndexOf() for performance reasons:
public static int NthIndexOf(this string s, string value, int n)
{
var index = -1;
for (; n > 0; n--)
index = s.IndexOf(value, index + 1);
return index;
}
This returns the 0-based start index of the (1-based) nth occurence of the string value.
Here is a working example: https://dotnetfiddle.net/xlqf2B

Where is the flaw in my algorithm to get the largest palindrome is a string representation of a number?

I'm trying to get the largest palindrome that can be formed by k replacements of digits in the string number.
e.g.
number="3943",k=1 --> "3993"
For that exact test case I am getting "393" and for some test cases I am getting an error like
Unhandled Exception: System.InvalidOperationException: Sequence
contains no elements at System.Linq.Enumerable.Last[TSource]
(IEnumerable`1 source) <0x414ec920 + 0x001ab> in :0
at Solution.LargestPalindrome (System.String numstr, Int32 k)
[0x00197] in solution.cs:74 at
Solution+c__AnonStorey0.<>m__0 (System.String str)
[0x00009] in solution.cs:61
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Solution
{
static bool IsPalindrome(string s)
{
// returns true or false depending on whether the string
// s is a palindrome
// e.g. "abba" --> true, "acba" --> false
for(int i = 0, j = s.Length - 1; i < j; ++i, --j)
{
if(s[i] != s[j])
return false;
}
return true;
}
static string Replace(string s, int i, char c)
{
// returns a copy of s with the character at index i
// replaced by character c
// e.g. "george",2,"x" --> "gexrge"
string part1 = s.Length > 0 ? s.Substring(0, i) : string.Empty;
string part2 = i < (s.Length - 1) ? c.ToString() : string.Empty;
string part3 = (i + 1) < (s.Length - 1) ? s.Substring(i + 1, s.Length - i - 1) : string.Empty;
return part1 + part2 + part3;
}
static string LargestPalindrome(string numstr, int k)
{
// numstr: string representation of number
// k: maximum number of digit replacements allowed
// if no digit replacements allowed, return same string
if(k == 0)
return numstr;
// digrange will be {'0', '1', ..., '9'}
List<char> digrange = new List<char>();
for(char c = '0'; c <= '9'; ++c)
digrange.Add(c);
// possibilities will be all possibilities of replacing one digit from numstr
// e.g. numstr="02" --> possibilities={"12","22","32",...,"92","00","01","03","09"}
List<string> possibilities = new List<string>();
for(int i = 0; i < numstr.Length; ++i)
{
foreach(char dig in digrange.Where(d => d != numstr[i]))
{
possibilities.Add(Replace(numstr,i,dig));
}
}
// if k = 1, get all the strings in cumulativePossiblities that are palindromes;
// else, transform each into the largest palindrome formed by k - 1 character
// replacements of itself
var cumulativePossibilities = k == 1
? possibilities.Where(str => IsPalindrome(str))
: possibilities.Select(str => LargestPalindrome(str, k - 1)).Where(str => IsPalindrome(str));
// sort cumulativePossibilities in ascending order of the integer representation
// of the strings
cumulativePossibilities.ToList().Sort((s1,s2) => {
Int64 i1 = Int64.Parse(s1),
i2 = Int64.Parse(s2);
return (i1 > i2) ? 1 : ((i1 == i2) ? 0 : -1);
});
// get the last element of the now-sorted cumulativePossibilities,
// which will be the largest number represented by the possible strings
// or will be null if there are none
string largest = cumulativePossibilities.Last();
// return the largest or "-1" if there were none
return largest != null ? largest : "-1";
}
static void Main(String[] args)
{
string[] tokens_n = Console.ReadLine().Split(' ');
int k = Convert.ToInt32(tokens_n[1]);
string number = Console.ReadLine();
// use brute force algorithm to find largest palindrome of the string
// representation of the number after k replacements of characters
Console.WriteLine(LargestPalindrome(number,k));
}
}
Not very efficient method, but simple to implement; the key feature is using PalindromeSubstitutions (counting how many characters' substitutions prevents string from being palindrome) instead of IsPalindrome (just a fact if string is palindrome or not)
// How many characters should be substituted in order to
// turn the string into palindrom
private static int PalindromeSubstitutions(string value) {
if (string.IsNullOrEmpty(value))
return 0;
int result = 0;
for (int i = 0; i < value.Length / 2; ++i)
if (value[i] != value[value.Length - 1 - i])
result += 1;
return result;
}
// Let's test all substrings of size Length, Length - 1, ... , 2, 1
// until we find substring with required tolerance
private static string BestPalindromeSubstitutions(string value, int tolerance) {
for (int size = value.Length; size >= 1; --size)
for (int start = 0; start <= value.Length - size; ++start)
if (PalindromeSubstitutions(value.Substring(start, size)) <= tolerance)
return value.Substring(start, size);
return "";
}
private static string SubstituteToPalindrome(string value) {
if (string.IsNullOrEmpty(value))
return value;
StringBuilder sb = new StringBuilder(value);
for (int i = 0; i < value.Length / 2; ++i)
sb[value.Length - 1 - i] = sb[i];
return sb.ToString();
}
Test:
string input = "73943";
string best = BestPalindromeSubstitutions(input, 1);
string report =
string.Format("Best palindrome {0} -> {1}", best, SubstituteToPalindrome(best));
Output
Best palindrome 3943 -> 3993
The problem is a quite simple example of greedy algorithm. let's first count how many permutations are required (at minimum) to transform the number into palindrome.
int req = 0;
for(int i = 0; i <= (s.length()-1)/2; i++){
if (s[i] != s[s.length()-1-i] && i != s.length()-1-i) req++;
}
Now once it is done, let's go through digits from left to right once again: i goes through 0 to (s.length()-1)/2 inclusive. Consider the following cases ( here i is not the middle letter, that case we consider separately) :
s[i] == s[s.length()-i-1], it wasn't counted in req, so if k >= req + 2 and s[i] != '9', we change both letters to '9', and reduce k by 2, req remains unchanged. But note that we guaranteed that there are enough operations left to make sure that number can be turned into palindrome (if it was initially possible)
s[i] != s[s.length()-i-1] - now if k == req or one of the letters is '9', then do the following: s[i]=s[s.length()-i-1]=max({s[i], s[s.length()-i-1]}). Reduce both k and req by 1.
Now if k > req and both letters are not '9', we change them both to 9. k -= 2, req -= 1.
Now if i = s.length()-i-1 and k > 0, change this letter s[i] to '9'.
The result u get at the end is what u are looking for.
Total complexity is O(n).

String Combinations With Character Replacement

I am trying to work through a scenario I haven't seen before and am struggling to come up with an algorithm to implement this properly. Part of my problem is a hazy recollection of the proper terminology. I believe what I am needing is a variation of the standard "combination" problem, but I could well be off there.
The Scenario
Given an example string "100" (let's call it x), produce all combinations of x that swap out one of those 0 (zero) characters for a o (lower-case o). So, for the simple example of "100", I would expect this output:
"100"
"10o"
"1o0"
"1oo"
This would need to support varying length strings with varying numbers of 0 characters, but assume there would never be more than 5 instances of 0.
I have this very simple algorithm that works for my sample of "100" but falls apart for anything longer/more complicated:
public IEnumerable<string> Combinations(string input)
{
char[] buffer = new char[input.Length];
for(int i = 0; i != buffer.Length; ++i)
{
buffer[i] = input[i];
}
//return the original input
yield return new string(buffer);
//look for 0's and replace them
for(int i = 0; i != buffer.Length; ++i)
{
if (input[i] == '0')
{
buffer[i] = 'o';
yield return new string(buffer);
buffer[i] = '0';
}
}
//handle the replace-all scenario
yield return input.Replace("0", "o");
}
I have a nagging feeling that recursion could be my friend here, but I am struggling to figure out how to incorporate the conditional logic I need here.
Your guess was correct; recursion is your friend for this challenge. Here is a simple solution:
public static IEnumerable<string> Combinations(string input)
{
int firstZero = input.IndexOf('0'); // Get index of first '0'
if (firstZero == -1) // Base case: no further combinations
return new string[] { input };
string prefix = input.Substring(0, firstZero); // Substring preceding '0'
string suffix = input.Substring(firstZero + 1); // Substring succeeding '0'
// e.g. Suppose input was "fr0d00"
// Prefix is "fr"; suffix is "d00"
// Recursion: Generate all combinations of suffix
// e.g. "d00", "d0o", "do0", "doo"
var recursiveCombinations = Combinations(suffix);
// Return sequence in which each string is a concatenation of the
// prefix, either '0' or 'o', and one of the recursively-found suffixes
return
from chr in "0o" // char sequence equivalent to: new [] { '0', 'o' }
from recSuffix in recursiveCombinations
select prefix + chr + recSuffix;
}
This works for me:
public IEnumerable<string> Combinations(string input)
{
var head = input[0] == '0' //Do I have a `0`?
? new [] { "0", "o" } //If so output both `"0"` & `"o"`
: new [] { input[0].ToString() }; //Otherwise output the current character
var tails = input.Length > 1 //Is there any more string?
? Combinations(input.Substring(1)) //Yes, recursively compute
: new[] { "" }; //Otherwise, output empty string
//Now, join it up and return
return
from h in head
from t in tails
select h + t;
}
You don't need recursion here, you can enumerate your patterns and treat them as binary numbers. For example, if you have three zeros in your string, you get:
0 000 ....0..0....0...
1 001 ....0..0....o...
2 010 ....0..o....0...
3 011 ....0..o....o...
4 100 ....o..0....0...
5 101 ....o..0....o...
6 110 ....o..o....0...
7 111 ....o..o....o...
You can implement that with bitwise operators or by treating the chars that you want to replace like an odometer.
Below is an implementation in C. I'm not familiar with C# and from the other answers I see that C# already has suitable standard classes to implement what you want. (Although I'm surprised that so many peolpe propose recursion here.)
So this is more an explanation or illustration of my comment to the question than an implementation advice for your problem.
int binrep(char str[])
{
int zero[40]; // indices of zeros
int nzero = 0; // number of zeros in string
int ncombo = 1; // number of result strings
int i, j;
for (i = 0; str[i]; i++) {
if (str[i] == '0') {
zero[nzero++] = i;
ncombo <<= 1;
}
}
for (i = 0; i < ncombo; i++) {
for (j = 0; j < nzero; j++) {
str[zero[j]] = ((i >> j) & 1) ? 'o' : '0';
}
printf("%s\n", str); // should yield here
}
return ncombo;
}
Here's a solution using recursion, and your buffer array:
private static void Main(string[] args)
{
var a = Combinations("100");
var b = Combinations("10000");
}
public static IEnumerable<string> Combinations(string input)
{
var combinations = new List<string>();
combinations.Add(input);
for (int i = 0; i < input.Length; i++)
{
char[] buffer= input.ToArray();
if (buffer[i] == '0')
{
buffer[i] = 'o';
combinations.Add(new string(buffer));
combinations = combinations.Concat(Combinations(new string(buffer))).ToList();
}
}
return combinations.Distinct();
}
The method adds the raw input as the first result. After that, we replace in a loop the 0s we see as a o and call our method back with that new input, which will cover the case of multiple 0s.
Finally, we end up with a couple duplicates, so we use Distinct.
I know that the earlier answers are better. But I don't want my code to go to waste. :)
My approach for this combinatorics problem would be to take advantage of how binary numbers work. My algorithm would be as follows:
List<string> ZeroCombiner(string str)
{
// Get number of zeros.
var n = str.Count(c => c == '0');
var limit = (int)Math.Pow(2, n);
// Create strings of '0' and 'o' based on binary numbers from 0 to 2^n.
var binaryStrings = new List<string>();
for (int i = 0; i < limit; ++i )
{
binaryStrings.Add(Binary(i, n + 1));
}
// Replace each zero with respect to each binary string.
var result = new List<string>();
foreach (var binaryString in binaryStrings)
{
var zeroCounter = 0;
var combinedString = string.Empty;
for (int i = 0; i < str.Length; ++i )
{
if (str[i] == '0')
{
combinedString += binaryString[zeroCounter];
++zeroCounter;
}
else
combinedString += str[i];
}
result.Add(combinedString);
}
return result;
}
string Binary(int i, int n)
{
string result = string.Empty;
while (n != 0)
{
result = result + (i % 2 == 0 ? '0' : 'o');
i = i / 2;
--n;
}
return result;
}

list produces specific set of combinations

I have a C# list:
A
B
A
C
D
A
How can I find all the combinations of letters in which the index of the following letter is always bigger. So in this case, combinations could be: A, AB, ABA, C, CD, ADA... BUT NOT DC, CB... since B index comes before C index for last example. Using indexes, 1, 12, 123, 146, 134 would be accepted, but something like 143 would not be accepted since 4 is greater than 3.
Simply generate all nonempty subsets of set {1,2,3,4,5,6}. For each such subset, just take its numbers (in increasing order) and convert them to the corresponding letters. This way you'll get all possible letter sequences. Then you'll have to remove duplicates, if necessary - for example A will be generated three times by sets {1}, {3} and {6}.
This code generates all the combination ( as the sequence of list ) :
static void Main(string[] args)
{
GetCombination(new List<char> { 'A','B','C' });
Console.ReadKey();
}
static void GetCombination(List<char> list)
{
for (int i = 1; i < Convert.ToInt32(Math.Pow(2, list.Count)); i++)
{
int temp = i;
string str = "";
int j = Convert.ToInt32( Math.Pow(2, list.Count - 1));
int index = 0;
while (j > 0)
{
if (temp - j >= 0)
{
str += list[index];
temp -= j;
}
j /= 2;
index++;
}
Console.WriteLine(str);
}
}
And the output is :
C
B
BC
A
AC
AB
ABC

using a char value to compare to a variable name in c#?

I am trying to solve one of the Euler Project problems using c# (problem 22). Though I have run into a problem. It's probably worth noting I am relatively new to programming, especially c#.
I need to come up with a word score for a set of strings that I have. This involves summing up the score of each letter in a a word, e.g. a=1, b=2, c=3 and so on. To do this I have assigned all 26 letters of the alphabet as variables with the relevant scores. I then want to compare each letter in the word with the relevant variables of the same name. However what I am left with is a char data type, what's the best way for me to compare the character to the relevant variable, and then use the variable value in my integer calculation. I have included the code I have so far below, with the problem occuring in the last 2 lines excluding braces. (I have had a quick look, and it appears this is not supported in c++, though i'm not sure about c#). Any help would be greatly appreciated.
string[] lines = File.ReadAllLines(#"C:\Users\john\Downloads\names.txt");
//Console.WriteLine(lines[1]);
char[] delimiterChars = { ',', '\t' };
string text = lines[0];
string[] names = text.Split(delimiterChars);
Console.WriteLine("{0} words in text:", names.Length);
Array.Sort(names);
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int f = 6;
int g = 7;
int h = 8;
int i = 9;
int j = 10;
int k = 11;
int l = 12;
int m = 13;
int n = 14;
int o = 15;
int p = 16;
int q = 17;
int r = 18;
int s = 19;
int t = 20;
int u = 21;
int v = 22;
int w = 23;
int x = 24;
int y = 25;
int z = 26;
int[] nameTotal;
for (int count = 0; count < names.Length; count++)
{
string name = names[count];
int total = 0;
for (int count2 = 0; count2 < name.Length; count2++)
{
nameTotal[count] = name.Substring(count2) + total;
total = total + nameTotal[count];
}
}
You can do this by taking advantage of the layout of the standard ASCII table.
In ASCII, the 'a' character has a decimal value of 97. Lower-case letters then continue up until 122.
Therefore, you can easily convert an 'a' char value to your required value by using:
char charToConvert = 'a';
int requiredValue = (int)charToConvert - 96;
If you want to calculate the sum of the letters in a name, you can also use Linq (just an example):
string name = "ABCD";
int sum = name.Select(letter => letter - 'A' + 1).Sum();
You can perform calculations with letters just like with integers in C#.
The Select method (which is an extension method from Linq) projects each letter of the string to it's corresponding value. These values are then summed by the extension method Sum().
Edit: As jlafay pointed out, you can omit the Select call and put the projection into the Sum method:
name.Sum(letter => letter - 'A' + 1)
And regarding your original question: You can't access the name of a local variable, even with reflection. That information is not included in the metadata of the compiled code.
Instead of assigning 26 variables for each letter, create an IDictionary<TKey, TValue> where the TKey is the character and the TValue is whatever value you assign. Then you can access the value by passing in the character much more easily.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SO
{
static class Program
{
static void Main()
{
WebClient wc = new WebClient();
var text = wc.DownloadString("http://projecteuler.net/project/names.txt");
var names = Regex.Matches(text, "[A-Z]+").Cast<Match>()
.Select(x => x.Value)
.OrderBy(x => x)
.Select((name, inx) => new
{
Name = name,
Score = name.Sum(c => c - 'A' + 1) * (inx + 1)
});
foreach (var n in names)
{
Console.WriteLine("{0}: {1}", n.Name, n.Score);
}
}
}
}

Categories