Trim a char array - c#

Background: I was invited to an interview at a high profile company and I was asked the following question before being told I failed the interview for the position (C#,mvc3,razor). I'm genuinely interested in how to solve this.
Question: "Write a method that takes a char array, trims whitespace, and returns the same array." After some thinking I was told to replace the whitespace with "\o".
I started with:
public static char[] Trim(char[] c)
{
for (int i = 0; i < c.Length; i++)
{
if (c[i] == '\r' || c[i] == '\n' || c[i] == '\t')
{
c[i] = '\o';
}
}
}
I was told I have to use the same array, can't put it in a list and call ToArray(). However I think if the array stays the same size it is impossible to "trim it".

They may have meant \0 (NUL character), not dash-0

Assuming they meant to replace whitespace characters with null characters then the solution is simple:
Step 1: From the start of the string (represented as a character array) replace whitespace characters until a non-WS character is encountered.
Step 2: From the end of the string, working backwards, do the same.
public static void Trim( Char[] chars )
{
int maxIdx = 0; // an optimization so it doesn't iterate through chars already encountered
for( int i = 0;i < chars.Length; i++ )
{
if( Char.IsWhitespace( chars[i] ) )
{
chars[i] = '\0';
}
else
{
maxIdx = i;
break;
}
}
for( int i = chars.Length - 1; i > maxIdx; i-- )
{
if( Char.IsWhitespace( chars[i] ) ) chars[i] = '\0';
}
}

public static char[] Trim(char[] str)
{
return str.Where(x => !Char.IsWhiteSpace(x)).ToArray();
}

This is ugly and untested, but it does the whole thing in a single pass without creating a new array:
public static void Trim(Char[] str) {
int nonNullIndex = 0;
int lastNonNullIndex = 0;
for(int i=0;i<str.Length;i++) {
str[nonNullIndex] = str[i];
if( !Char.IsWhitespace( str[i] ) || nonNullIndex > 0) nonNullIndex++;
if( !Char.IsWhitespace( str[i] )) lastNonNullIndex = i;
}
nonNullIndex++
str[lastNonNullIndex] = '\0';
}

I guess what you might have been asked is to remove white space from between the string and then fill the char array for the remaining elements of the array with '\0'
e.g. "Convert this string" to "Convertthisstring" and fill the remaining array with 2 '\0'
Solution:
char[] TrimWhiteSpace(char[] source)
{
int i, j = 0;
for (i = 0; i < source.Length; i++)
{
if (!char.IsWhiteSpace(source[i]))
{
source[j] = source[i];
j++;
}
}
for (int x = 0; x < (i - j); x++)
{
source[j + x] = '\0';
}
return source;
}

Related

I need to check if a string is a pangram string or not is my code correct?

public static class Inova
{
public static bool IsPangram(string str)
{
int compteur = 26;
for (int i = 0; i <= str.Length; i++)
{
if (('A' <= str[i] && str[i] <= 'Z') || ('a' <= str[i] && str[i] <= 'z'))
{
for (int j = str[i + 1]; j <= str.Length; j++)
{
if (compteur != 0 && str[i] != str[j])
{
compteur = compteur - 1;
}
}
}
if (compteur == 0)
{
return true;
}
else
{
return false;
}
}
}
}
There are multiple things incorrect:
for (int j = str[i + 1]; j <= str.Length; j++)
this does not do what you think, it will convert the next char to an int, you want to loop all letters until end, beginning from the current letter + 1.
The if ... else belong to the end of the method, outside of the loop, otherwise you return false after the first iteration in the for-loop
So you want to know if it's a perfect pangram? First we need to say what a pangram is: a sentence containing every letter of the alphabet. It seems you want to check if it's even a perfect pangram, so every letter should appear exactly once. Here is a method not using any fancy LINQ(which might not be allowed) that supports perfect/imperfect pangrams:
public static class Inova
{
private const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static bool IsPangram(string str, bool mustBePerfect)
{
HashSet<char> remaingLetters = new HashSet<char>(alphabet);
foreach (char c in str)
{
char letter = char.ToUpperInvariant(c);
if (!alphabet.Contains(letter)) continue;
bool repeatingLetter = !remaingLetters.Remove(letter);
if (mustBePerfect && repeatingLetter)
{
return false; // already removed
}
}
return remaingLetters.Count == 0;
}
}
Usage:
bool isPangram = Inova.IsPangram("abcdefghijklmnopqrstuvwxyZZ", false);
Since z appears twice this method returns false for perfect and true for not perfect.
Demo: https://dotnetfiddle.net/gEXuvG
Side-note: i wanted to keep it simple, if you want you can still improve it. You can return true in the loop: if(!mustBePerfect && remaingLetters.Count == 0) return true.
I would check for existence of each letter in the string, so
public static bool IsPangram(string str) {
str = str.ToLower();
for (int i = 0; i < 26; i++) {
char c = Convert.ToChar(i + 97);
if (!str.Contains(c)) {
return false;
}
}
return true;
}
Console.WriteLine(IsPangram("hello world"));
Console.WriteLine(IsPangram("abcdefghi jkl mno pqrstuvwxyz"));
// output:
False
True

The splitting of a string without using String.Split method does not return correct result

I want to split a string without using the String.Split method.
I found a possible solution here. The code that I use is from the second answer.
This is my code:
public string[] SplitString(string input, char delimiter)
{
List<String> parts = new List<String>();
StringBuilder buff = new StringBuilder();
for (int i = 0; i < input.Length; i++)
{
if (input[i] == delimiter)
{
parts.Add(buff.ToString());
buff.Clear();
}
else
{
buff.Append(input[i]);
}
}
return parts.ToArray();
}
My problem here is that when I try to split a string like this
dog cat car person by " ", the result contains the words without the last one (in this example - person).
If after the last word there is a white space, the result is correct.
I tried to add something like i == input.Length when the for loop is from 0 to i <= input.Length. But the result was still without the last word.
Am I missing something?
Add another parts.Add(buff.ToString()); after exiting the loop - to flush the last word into the collection. You can check the length before doing so or not as commented and explained why by #hvd.
if(buff.Length != 0)
{
parts.Add(buff.ToString());
}
return parts.ToArray();
Another approach, instead of using a StringBuilder would be:
public static string[] SplitString(string input, char delimiter)
{
List<String> parts = new List<String>();
int start = 0;
for(int i = 0; i < input.Length; i++)
{
if(input[i] == delimiter)
{
parts.Add(input.Substring(start, i - start));
start = i + 1;
}
}
parts.Add(input.Substring(start, input.Length - start));
return parts.ToArray();
}
Or use yield return and return IEnumerable<string>:
public static IEnumerable<string> SplitString(string input, char delimiter)
{
int start = 0;
for(int i = 0; i < input.Length; i++)
{
if(input[i] == delimiter)
{
yield return input.Substring(start, i - start);
start = i + 1;
}
}
yield return input.Substring(start, input.Length - start);
}
Here is what you are missing in your code after for loop:
for (int i = 0; i < input.Length; i++)
{
if (input[i] == delimiter)
{
parts.Add(buff.ToString());
buff.Clear();
}
else
{
buff.Append(input[i]);
}
}
// This you need to add
if (!string.IsNullOrEmpty(buff.ToString()))
{
parts.Add(buff.ToString());
}
return parts.ToArray();

Intersect two lists and return the similarity with the preserved order of the original first string value

I'm facing a problem I don't even know what to search in Google/Stack Overflow.
So comment if you feel the need for further explanation, questions.
Basically I want to intersect two lists and return the similarity with the preserved order of the original first string value.
Example:
I have two strings, that I convert to a CharArray.
I want to Intersect these two arrays and return the values that are similar, including/with the order of the first string (s1).
As you can see the first string contains E15 (in that specific order), and so does the seconds one.
So these two strings will return : { 'E', '1', '5' }
string s1 = "E15QD(A)";
string s2 = "NHE15H";
The problem I am facing is that if i replace "s2" with:
string s2 = "NQE18H" // Will return {'Q', 'E', '1' }
My operation will return : {'Q', 'E', '1' }
The result should be : {'E', '1' } because Q don't follow the letter 1
Currently my operation is not the greatest effort, because i don't know which methods to use in .NET to be able to do this.
Current code:
List<char> cA1 = s1.ToList();
List<char> cA2 = s2.ToList();
var result = cA1.Where(x => cA2.Contains(x)).ToList();
Feel free to help me out, pointers in the right direction is acceptable as well as a full solution.
This is a "longest common substring" problem.
You can use this extension to get all substrings lazily:
public static class StringExtensions
{
public static IEnumerable<string> GetSubstrings(this string str)
{
if (string.IsNullOrEmpty(str))
throw new ArgumentException("str must not be null or empty", "str");
for (int c = 0; c < str.Length - 1; c++)
{
for (int cc = 1; c + cc <= str.Length; cc++)
{
yield return str.Substring(c, cc);
}
}
}
}
Then it's easy and readable with this LINQ query:
string longestIntersection = "E15QD(A)".GetSubstrings()
.Intersect("NQE18H".GetSubstrings())
.OrderByDescending(s => s.Length)
.FirstOrDefault(); // E1
Enumerable.Intersect is also quite efficient since it's using a set. One note: if one both strings is larger than the other then it's more efficient(in terms of memory) to use it first:
longString.GetSubstrings().Intersect(shortString.GetSubstrings())
I think this should do it:
string similar = null;
for (int i = 0; i < s1.Length; i++)
{
string s = s1.Substring(0, i + 1);
if (s2.Contains(s))
{
similar = s;
}
}
char[] result = similar.ToCharArray();
#TimSchmelter provided the link to this answer in the comments of the original post.
public int LongestCommonSubstring(string str1, string str2, out string sequence)
{
sequence = string.Empty;
if (String.IsNullOrEmpty(str1) || String.IsNullOrEmpty(str2))
return 0;
int[,] num = new int[str1.Length, str2.Length];
int maxlen = 0;
int lastSubsBegin = 0;
StringBuilder sequenceBuilder = new StringBuilder();
for (int i = 0; i < str1.Length; i++)
{
for (int j = 0; j < str2.Length; j++)
{
if (str1[i] != str2[j])
num[i, j] = 0;
else
{
if ((i == 0) || (j == 0))
num[i, j] = 1;
else
num[i, j] = 1 + num[i - 1, j - 1];
if (num[i, j] > maxlen)
{
maxlen = num[i, j];
int thisSubsBegin = i - num[i, j] + 1;
if (lastSubsBegin == thisSubsBegin)
{//if the current LCS is the same as the last time this block ran
sequenceBuilder.Append(str1[i]);
}
else //this block resets the string builder if a different LCS is found
{
lastSubsBegin = thisSubsBegin;
sequenceBuilder.Length = 0; //clear it
sequenceBuilder.Append(str1.Substring(lastSubsBegin, (i + 1) - lastSubsBegin));
}
}
}
}
}
sequence = sequenceBuilder.ToString();
return maxlen;
}

How can I separate the sequential print statements from nested for-loops?

I have the following method which prints out all the contiguous subsets of the given array. I want to be able to separate out the ugly print statement interwoven in the for loops and into a new function. Is this doable?
// Example:
// Input: char[] input = new char[] { 'a', 'b', 'c' };
// Output:
// (a) (a b) (a b c)
// (b) (b c)
// (c)
static void PrintContSubArrays(char[] arr)
{
int len = arr.Length;
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < len; i++)
{
for (int j = 1; j <= len; j++)
{
sb2.AppendFormat("(");
for (int k = i; k < j; k++)
{
sb2.AppendFormat("{0} ", arr[k]);
}
sb2.Remove(sb2.Length - 1, 1);
sb2.Append(") ");
if (sb2.Length > 3) sb1.Append(sb2);
sb2.Clear();
}
sb1.Append(System.Environment.NewLine);
}
Console.WriteLine(sb1.ToString());
Console.ReadLine();
}
Implemented this based on suggestion by #usr
Instead of doing the string processing inline extract the subarrays as
char[] and return them using yield return. That way the caller
receives a stream of subarrays. He can process, format and print them
as he wants.
This is what I have. But this does not translate to your exact output format. Some formatting is lost due to loss of context. If you are willing to pass in the original array length into the print method, you could get exactly what you need by tweaking the for-loop in print method.
// Input: int[] input = new int[] { 1, 2, 3};
// Call: Console.WriteLine(PrintContSubArrays(GetContSubArrays(input)));
// Output:
// (1)
// (1 2)
// (1 2 3)
// (2)
// (2 3)
// (3)
// Generate subsets
static IEnumerable<int[]> GetContSubArrays(int[] arr)
{
int len = arr.Length;
for (int i = 0; i < len; i++)
{
for (int j = 1; j <= len; j++)
{
int[] placeholder = new int[j - i < 0 ? 0 : j - i];
bool isPlaceholderEmpty = true;
for (int k = i; k < j; k++)
{
placeholder[k - i] = arr[k];
isPlaceholderEmpty = false;
}
if (!isPlaceholderEmpty) yield return placeholder;
}
}
}
// Print
static string PrintContSubArrays(IEnumerable<int[]> input)
{
StringBuilder sb1 = new StringBuilder();
foreach (int[] intarr in input)
{
if (intarr != null)
{
sb1.Append("(");
foreach (int intsingle in intarr)
{
sb1.AppendFormat("{0} ", intsingle);
}
sb1.Remove(sb1.Length - 1, 1);
sb1.Append(")");
}
sb1.AppendFormat(Environment.NewLine);
}
return sb1.ToString();
}
Instead of doing the string processing inline extract the subarrays as char[] and return them using yield return. That way the caller receives a stream of subarrays. He can process, format and print them as he wants.
Here's one way to simplify things:
static void PrintContiguousSubArrays( char[] input )
{
if ( input == null ) throw new ArgumentNullException("input") ;
if ( input.Length == 0 ) return ;
for ( int offset = 0 ; offset < input.Length ; ++offset )
{
string separator = "" ;
for ( int length = 1 ; offset+length <= input.Length ; ++length )
{
Console.Write( "{0}[{1}]" ,
separator ,
string.Join("," , input.Skip(offset).Take(length).Select( c => new string(c,1) ) )
);
separator = " ";
}
Console.WriteLine();
}
return ;
}
Here's another way:
static IEnumerable<IEnumerable<char>> ContiguousSubArraysOf( char[] input )
{
if ( input == null ) throw new ArgumentNullException("input") ;
for ( int offset = 0 ; offset < input.Length ; ++offset )
{
for ( int length = 1 ; offset+length <= input.Length ; ++length )
{
yield return input.Skip(offset).Take(length) ;
}
}
}
This will let you say something like this:
char[] input = "abc".ToCharArray();
foreach( var item in ContiguousSubArraysOf(input) )
{
Console.WriteLine( "[{0}]" ,
string.Join( "," , item.Select( c => new string(c,1) ) )
) ;
}
How about doing this?
static void PrintContSubArrays(char[] arr)
{
Console.WriteLine(String.Join(Environment.NewLine,
from n1 in Enumerable.Range(0, arr.Length)
select String.Join(" ",
from n2 in Enumerable.Range(1, arr.Length - n1)
select String.Format("({0})", String.Join(" ", arr.Skip(n1).Take(n2))))));
Console.ReadLine();
}
This way you can get rid of all of the ugly loops too.

Longest Common Subsequence

Hi this is my code for longest common subsequence for 2 strings in c# . I need help in backtracking . I need to find out the subsequence : GTCGT
String str1 = "GTCGTTCG";
String str2 = "ACCGGTCGAGTG";
int[,] l = new int[str1.Length, str2.Length]; // String 1 length and string 2 length storing it in a 2-dimensional array
int lcs = -1;
string substr = string.Empty;
int end = -1;
for (int i = 0; i <str1.Length ; i++) // Looping based on string1 length
{
for (int j = 0; j < str2.Length; j++) // Looping based on string2 Length
{
if (str1[i] == str2[j]) // if match found
{
if (i == 0 || j == 0) // i is first element or j is first elemnt then array [i,j] = 1
{
l[i, j] = 1;
}
else
{
l[i, j] = l[i - 1, j - 1] + 1; // fetch the upper value and increment by 1
}
if (l[i, j] > lcs)
{
lcs = l[i, j]; // store lcs value - how many time lcs is found
end = i; // index on longest continuous string
}
}
else // if match not found store zero initialze the array value by zero
{
l[i, j] = 0;
}
}
Your function needs to return a collection of strings. There might be several longest common sub-sequence with same length.
public List<string> LCS(string firstString, string secondString)
{
// to create the lcs table easier which has first row and column empty.
string firstStringTemp = " " + firstString;
string secondStringTemp = " " + secondString;
// create the table
List<string>[,] temp = new List<string>[firstStringTemp.Length, secondStringTemp.Length];
// loop over all items in the table.
for (int i = 0; i < firstStringTemp.Length; i++)
{
for (int j = 0; j < secondStringTemp.Length; j++)
{
temp[i, j] = new List<string>();
if (i == 0 || j == 0) continue;
if (firstStringTemp[i] == secondStringTemp[j])
{
var a = firstStringTemp[i].ToString();
if (temp[i - 1, j - 1].Count == 0)
{
temp[i, j].Add(a);
}
else
{
foreach (string s in temp[i - 1, j - 1])
{
temp[i, j].Add(s + a);
}
}
}
else
{
List<string> b = temp[i - 1, j].Concat(temp[i, j - 1]).Distinct().ToList();
if (b.Count == 0) continue;
int max = b.Max(p => p.Length);
b = b.Where(p => p.Length == max).ToList();
temp[i, j] = b;
}
}
}
return temp[firstStringTemp.Length - 1, secondStringTemp.Length - 1];
}
You need to have a collection set in each entry of table. So you can still keep different strings with the same length in each cell of table.
As far as I've understood your question, I think you want to know the subsequence value i.e. that string. So, to get the subsequence, I've learnt a little bit differently. First, I calculate the table the one we do in standard Longest Common Subsequence (LCS) problem. Then I traverse the table to get the subsequence value. Sorry, I'm not familiar with C#, so, I will give you CPP code. Please have a look and let me know if you face any problem.
#include<iostream>
#include<vector>
#include<string>
using namespace std;
string printLongestCommonSubsequence(vector<vector<int> >& dp, int m, int n, string text1, string text2){
int i = m, j = n;
string lcs = "";
while(i > 0 && j > 0){
if(text1[i-1] == text2[j-1]){
lcs.push_back(text1[i-1]);
i--; j--;
}
else{
if(dp[i][j-1] > dp[i-1][j]) j--;
else i--;
}
}
reverse(lcs.begin(), lcs.end());
return lcs;
}
string longestCommonSubsequence(string text1, string text2){
int m = text1.size();
int n = text2.size();
vector<vector<int> > dp(m+1, vector<int>(n+1));
//initialization
for(int i=0; i<m+1; i++){
for(int j=0; j<n+1; j++){
if(i == 0 || j == 0) dp[i][j] = 0;
}
}
//solving the subproblems to solve the bigger problems
for(int i=1; i<m+1; i++){
for(int j=1; j<n+1; j++){
if(text1[i-1] == text2[j-1])
dp[i][j] = 1 + dp[i-1][j-1];
else
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
return printLongestCommonSubsequence(dp, m, n, text1, text2);
}
int main(){
string text1, text2;
cout<<"Enter the first string: ";
cin>>text1;
cout<<"\nEnter the second string: ";
cin>>text2;
string lcs = longestCommonSubsequence(text1, text2);
cout<<"Longest Common Subsequence is: "<<lcs<<endl;
return(0);
}
Please have a look at the diagram.
With respect to printing the LCS,
The basic idea is:
When the characters are equal of both the strings then move towards diagonal.
When the characters are not equal of both the strings then move towards the maximum of both the directions.
I hope this helps 🙂
Happy Learning
Thanks

Categories

Resources