Cross Search generate char Matrix - c#

I am trying to create a word search puzzle matrix, this is the code I have,
static void PlaceWords(List<string> words)
{
Random rn = new Random();
foreach (string p in words)
{
String s = p.Trim();
bool placed = false;
while (placed == false)
{
int nRow = rn.Next(0,10);
int nCol = rn.Next(0,10);
int nDirX = 0;
int nDirY = 0;
while (nDirX == 0 && nDirY == 0)
{
nDirX = rn.Next(3) - 1;
nDirY = rn.Next(3) - 1;
}
placed = PlaceWord(s.ToUpper(), nRow, nCol, nDirX, nDirY);
}
}
}
static bool PlaceWord(string s, int nRow, int nCol, int nDirX, int nDirY)
{
bool placed = false;
int LetterNb = s.Length;
int I = nRow;
int J = nCol;
if (MatriceIndice[nRow, nCol] == 0)
{
placed = true;
for (int i = 0; i < s.Length-1; i++)
{
I += nDirX;
J += nDirY;
if (I < 10 && I>0 && J < 10 && J>0)
{
if (MatriceIndice[I, J] == 0)
placed = placed && true;
else
placed = placed && false;
}
else
{
return false;
}
}
}
else
{
return false;
}
if(placed==true)
{
int placeI = nRow;
int placeJ = nCol;
for (int i = 0; i < s.Length - 1; i++)
{
placeI += nDirX;
placeJ += nDirY;
MatriceIndice[placeI,placeJ] = 1;
MatriceChars[placeJ, placeJ] = s[i];
}
}
return placed;
}
However it seems like it is an infinite loop. I am trying to add the code in a 1010 char matrix linked to a 1010 int matrix initially filled with 0 where I change the cases to 1 if the word is added to the matrix. How should I fix the code?

There are several errors. First,
MatriceChars[placeJ, placeJ] = s[i];
should be
MatriceChars[placeI, placeJ] = s[i];
Second,
for (int i = 0; i < s.Length - 1; i++)
(two occurrences) should be
for (int i = 0; i < s.Length; i++)
(You do want all the letters in the words, right?)
Third, when testing indices, you should use I >= 0, not I > 0, as the matrix indices start at 0.
However, the main logic of the code seems to work, but if you try to place too many words, you will indeed enter an infinite loop, since it just keeps trying and failing to place words that can never fit.

Related

Check patterns of 5 in password "12345", "abcde"

I'm trying to validate a password string in a .NET for sequential patterns (forward or reverse) with numbers or letters of 5 or more.
Examples of patterns that will not be accepted:
"ABCDE",
"12345",
"54321",
"edcba"
I cannot find a decent regex pattern that handles finding the characters in order, currently just returning any sequence of 5 letters or numbers:
public bool CheckForSequence(string input)
{
return Regex.IsMatch(input.ToUpper(), #"([A-Z])(?!\1)([A-Z])(?!\1|\2)([A-Z])(?!\1|\2|\3)([A-Z])(?!\1|\2|\3|\4)([A-Z])") ||
Regex.IsMatch(input, #"([1-9])(?!\1)([1-9])(?!\1|\2)([1-9])(?!\1|\2|\3)([1-9])(?!\1|\2|\3|\4)([1-9])");
}
There are probably way better ways to do this, but, just for fun, I've made a simple brute-force algorithm:
bool CheckForSequence(string inp) {
bool InRange(int c) {
const int minLower = (int)'a';
const int maxLower = (int)'z';
const int minUpper = (int)'A';
const int maxUpper = (int)'Z';
const int minNumber = (int)'0';
const int maxNumber = (int)'9';
return (c >= minLower && c <= maxLower) || (c >= minUpper && c <= maxUpper) || (c >= minNumber && c <= maxNumber);
}
if(inp.Length < 5) return false;
for(var i = 0; i < inp.Length - 4; i++)
{
var c = (int)inp[i];
if(InRange(c))
{
var vM = c;
int x;
for(x = i+1; x < i + 5; x++)
{
if(inp[x] != vM+1 || !InRange(inp[x])) break;
vM++;
}
if(x == i+5) return true;
for(x = i+1; x < i + 5; x++)
{
if(inp[x] != vM-1 || !InRange(inp[x])) break;
vM--;
}
if(x == i+5) return true;
}
}
return false;
}
You can see it in action in this fiddle
Wiktor is correct - regex is the wrong tool for this.
Here's one possible implementation:
public static class SequenceChecker
{
private static char MapChar(char c) => c switch
{
>= '0' and <= '9' => c,
>= 'A' and <= 'Z' => c,
>= 'a' and <= 'z' => (char)(c - 'a' + 'A'),
_ => default,
};
private static bool IsSequence(ReadOnlySpan<char> input)
{
char x = MapChar(input[0]);
if (x == default) return false;
char y = MapChar(input[1]);
if (y == default) return false;
int direction = y - x;
if (Math.Abs(direction) != 1) return false;
for (int index = 2; index < input.Length; index++)
{
x = y;
y = MapChar(input[index]);
if (y == default) return false;
int nextDirection = y - x;
if (nextDirection != direction) return false;
}
return true;
}
public static bool ContainsSequence(string input, int sequenceLength = 5)
{
if (sequenceLength < 2) throw new ArgumentOutOfRangeException(nameof(sequenceLength));
if (input is null) return false;
if (input.Length < sequenceLength) return false;
for (int startIndex = 0; startIndex < 1 + input.Length - sequenceLength; startIndex++)
{
if (IsSequence(input.AsSpan(startIndex, sequenceLength)))
{
return true;
}
}
return false;
}
}
Just to add to the plethora of solutions posted so far:
public static int LongestAscendingOrDescendingRun(string s)
{
if (s.Length <= 1)
return 0;
int longest = 0;
int current = 0;
bool ascending = false;
for (int i = 1; i < s.Length; i++)
{
bool isAscending () => s[i]-s[i-1] == +1;
bool isDescending() => s[i]-s[i-1] == -1;
if (current > 0)
{
if (ascending)
{
if (isAscending())
{
longest = Math.Max(longest, ++current);
}
else // No longer ascending.
{
current = 0;
}
}
else // Descending.
{
if (isDescending())
{
longest = Math.Max(longest, ++current);
}
else // No longer descending.
{
current = 0;
}
}
}
else // No current.
{
if (isAscending())
{
ascending = true;
current = 2;
longest = Math.Max(longest, current);
}
else if (isDescending())
{
ascending = false;
current = 2;
longest = Math.Max(longest, current);
}
}
}
return longest;
}
Like Wiktor has already said, regex isn't a good way to do this. You could find the difference between consecutive characters of the string, and complain if you find a sequence of four or more ones (or -1s).
public bool CheckForSequence(string pass)
{
int curr_diff = 0; // The difference between the i-1th and i-2th character
int consec_diff = 0; // The number of consecutive pairs having the same difference
for (int i = 1; i < pass.Length; i++)
{
int diff = pass[i] - pass[i - 1]; // The difference between the ith and i-1th character
if (Math.Abs(diff) == 1 && curr_diff == diff)
{
// If the difference is the same, increment consec_diff
// And check if the password is invalid
consec_diff++;
if (consec_diff >= 4)
return false;
}
else
{
// New diff. reset curr_diff and consec_diff
curr_diff = diff;
consec_diff = Math.Abs(diff)==1 ? 1 : 0;
// If the difference is 1, set consec_diff to 1 else 0
}
}
return consec_diff < 4;
}

Implementing and using MinMax with four in row (connect4) game

I'm trying to implement the MinMax algorithm for four in a row (or connect4 or connect four) game.
I think I got the idea of it, it should build a tree of possible boards up to a certain depth, evaluate them and return their score, then we just take the max of those scores.
So, aiChooseCol() checks the score of every possible column by calling MinMax() and returns the column with the max score.
Now I wasn't sure, is this the right way to call MinMax()?
Is it right to check temp = Math.Max(temp, 1000);?
I still haven't made the heuristic function but this should at least recognize a winning column and choose it, but currently it just choose the first free column from the left... I can't figure out what am I doing wrong.
private int AiChooseCol()
{
int best = -1000;
int col=0;
for (int i = 0; i < m_Board.Cols; i++)
{
if (m_Board.CheckIfColHasRoom(i))
{
m_Board.FillSignInBoardAccordingToCol(i, m_Sign);
int t = MinMax(5, m_Board, board.GetOtherPlayerSign(m_Sign));
if (t > best)
{
best = t;
col = i;
}
m_Board.RemoveTopCoinFromCol(i);
}
}
return col;
}
private int MinMax(int Depth, board Board, char PlayerSign)
{
int temp=0;
if (Depth <= 0)
{
// return from heurisitic function
return temp;
}
char otherPlayerSign = board.GetOtherPlayerSign(PlayerSign);
char checkBoard = Board.CheckBoardForWin();
if (checkBoard == PlayerSign)
{
return 1000;
}
else if (checkBoard == otherPlayerSign)
{
return -1000;
}
else if (!Board.CheckIfBoardIsNotFull())
{
return 0; // tie
}
if (PlayerSign == m_Sign) // maximizing Player is myself
{
temp = -1000;
for (int i = 0; i < Board.Cols; i++)
{
if (Board.FillSignInBoardAccordingToCol(i, PlayerSign)) // so we don't open another branch in a full column
{
var v = MinMax(Depth - 1, Board, otherPlayerSign);
temp = Math.Max(temp, v);
Board.RemoveTopCoinFromCol(i);
}
}
}
else
{
temp = 1000;
for (int i = 0; i < Board.Cols; i++)
{
if (Board.FillSignInBoardAccordingToCol(i, PlayerSign)) // so we don't open another branch in a full column
{
var v = MinMax(Depth - 1, Board, otherPlayerSign);
temp = Math.Min(temp, v);
Board.RemoveTopCoinFromCol(i);
}
}
}
return temp;
}
Some notes:
FillSignInBoardAccordingToCol() returns a boolean if it was successful.
The board type has a char[,] array with the actual board and signs of the players.
This code is in the AI Player class.
So I decided to write my own MinMax Connect 4. I used the depth to determine the value of a win or loss so that a move that gets you closer to winning or blocking a loss will take precedence. I also decide that I will randomly pick the move if more than one has the same heuristic. Finally I stretched out the depth to 6 as that's how many moves are required to find possible win paths from the start.
private static void Main(string[] args)
{
var board = new Board(8,7);
var random = new Random();
while (true)
{
Console.WriteLine("Pick a column 1 -8");
int move;
if (!int.TryParse(Console.ReadLine(), out move) || move < 1 || move > 8)
{
Console.WriteLine("Must enter a number 1-8.");
continue;
}
if (!board.DropCoin(1, move-1))
{
Console.WriteLine("That column is full, pick another one");
continue;
}
if (board.Winner == 1)
{
Console.WriteLine(board);
Console.WriteLine("You win!");
break;
}
if (board.IsFull)
{
Console.WriteLine(board);
Console.WriteLine("Tie!");
break;
}
var moves = new List<Tuple<int, int>>();
for (int i = 0; i < board.Columns; i++)
{
if (!board.DropCoin(2, i))
continue;
moves.Add(Tuple.Create(i, MinMax(6, board, false)));
board.RemoveTopCoin(i);
}
int maxMoveScore = moves.Max(t => t.Item2);
var bestMoves = moves.Where(t => t.Item2 == maxMoveScore).ToList();
board.DropCoin(2, bestMoves[random.Next(0,bestMoves.Count)].Item1);
Console.WriteLine(board);
if (board.Winner == 2)
{
Console.WriteLine("You lost!");
break;
}
if (board.IsFull)
{
Console.WriteLine("Tie!");
break;
}
}
Console.WriteLine("DONE");
Console.ReadKey();
}
private static int MinMax(int depth, Board board, bool maximizingPlayer)
{
if (depth <= 0)
return 0;
var winner = board.Winner;
if (winner == 2)
return depth;
if (winner == 1)
return -depth;
if (board.IsFull)
return 0;
int bestValue = maximizingPlayer ? -1 : 1;
for (int i = 0; i < board.Columns; i++)
{
if (!board.DropCoin(maximizingPlayer ? 2 : 1, i))
continue;
int v = MinMax(depth - 1, board, !maximizingPlayer);
bestValue = maximizingPlayer ? Math.Max(bestValue, v) : Math.Min(bestValue, v);
board.RemoveTopCoin(i);
}
return bestValue;
}
public class Board
{
private readonly int?[,] _board;
private int? _winner;
private bool _changed;
public Board(int cols, int rows)
{
Columns = cols;
Rows = rows;
_board = new int?[cols, rows];
}
public int Columns { get; }
public int Rows { get; }
public bool ColumnFree(int column)
{
return !_board[column, 0].HasValue;
}
public bool DropCoin(int playerId, int column)
{
int row = 0;
while (row < Rows && !_board[column,row].HasValue)
{
row++;
}
if (row == 0)
return false;
_board[column, row - 1] = playerId;
_changed = true;
return true;
}
public bool RemoveTopCoin(int column)
{
int row = 0;
while (row < Rows && !_board[column, row].HasValue)
{
row++;
}
if (row == Rows)
return false;
_board[column, row] = null;
_changed = true;
return true;
}
public int? Winner
{
get
{
if (!_changed)
return _winner;
_changed = false;
for (int i = 0; i < Columns; i++)
{
for (int j = 0; j < Rows; j++)
{
if (!_board[i, j].HasValue)
continue;
bool horizontal = i + 3 < Columns;
bool vertical = j + 3 < Rows;
if (!horizontal && !vertical)
continue;
bool forwardDiagonal = horizontal && vertical;
bool backwardDiagonal = vertical && i - 3 >= 0;
for (int k = 1; k < 4; k++)
{
horizontal = horizontal && _board[i, j] == _board[i + k, j];
vertical = vertical && _board[i, j] == _board[i, j + k];
forwardDiagonal = forwardDiagonal && _board[i, j] == _board[i + k, j + k];
backwardDiagonal = backwardDiagonal && _board[i, j] == _board[i - k, j + k];
if (!horizontal && !vertical && !forwardDiagonal && !backwardDiagonal)
break;
}
if (horizontal || vertical || forwardDiagonal || backwardDiagonal)
{
_winner = _board[i, j];
return _winner;
}
}
}
_winner = null;
return _winner;
}
}
public bool IsFull
{
get
{
for (int i = 0; i < Columns; i++)
{
if (!_board[i, 0].HasValue)
return false;
}
return true;
}
}
public override string ToString()
{
var builder = new StringBuilder();
for (int j = 0; j < Rows; j++)
{
builder.Append('|');
for (int i = 0; i < Columns; i++)
{
builder.Append(_board[i, j].HasValue ? _board[i,j].Value.ToString() : " ").Append('|');
}
builder.AppendLine();
}
return builder.ToString();
}
}

Index Of Longest Run C#

I am trying to solve this question:
Write a function that finds the zero-based index of the longest run in a string. A run is a consecutive sequence of the same character. If there is more than one run with the same length, return the index of the first one.
For example, IndexOfLongestRun("abbcccddddcccbba") should return 6 as the longest run is dddd and it first appears on index 6.
Following what i have done:
private static int IndexOfLongestRun(string str)
{
char[] array1 = str.ToCharArray();
//Array.Sort(array1);
Comparer comparer = new Comparer();
int counter =1;
int maxCount = 0;
int idenxOf = 0;
for (int i =0; i<array1.Length-1 ; i++)
{
if (comparer.Compare(array1[i],array1[i+1]) == 0)
{
counter++;
}
else {
if(maxCount < counter)
{
maxCount = counter;
idenxOf = i - counter + 1;
}
counter = 1;
}
}
return idenxOf ;
}
}
public class Comparer : IComparer<char>
{
public int Compare(char firstChar, char nextChar)
{
return firstChar.CompareTo(nextChar);
}
}
The problem is that when i get to the last index for example "abbccaaaaaaaaaa"
which is a in this case, and when i=14 (taking this string as example) and when i<array1.Length-1 statment is false, the for loop jumps directrly to return indexOf; and return the wrong index, I am trying to find out how to push the forloop to continue the implementation so idenxOf could be changed to the right index. Any help please?
You could check whether a new best score is achieved for each iteration when current == previous. Minimally slower, but it allows you to write shorter code by omitting an extra check after the loop:
int IndexOfLongestRun(string input)
{
int bestIndex = 0, bestScore = 0, currIndex = 0;
for (var i = 0; i < input.Length; ++i)
{
if (input[i] == input[currIndex])
{
if (bestScore < i - currIndex)
{
bestIndex = currIndex;
bestScore = i - currIndex;
}
}
else
{
currIndex = i;
}
}
return bestIndex;
}
Promote the loop variable i to method scope and repeat the conditional block if (maxCount < counter) { ... } right after the loop exit. Thus, it executes one more time after the loop completes
private static int IndexOfLongestRun(string str)
{
char[] array1 = str.ToCharArray();
//Array.Sort(array1);
Comparer comparer = new Comparer();
int counter = 1;
int maxCount = 0;
int idenxOf = 0;
int i;
for (i = 0; i < array1.Length - 1; i++)
{
if (comparer.Compare(array1[i], array1[i + 1]) == 0)
{
counter++;
}
else
{
if (maxCount < counter)
{
maxCount = counter;
idenxOf = i - counter + 1;
}
counter = 1;
}
}
if (maxCount < counter)
{
maxCount = counter;
idenxOf = i - counter + 1;
}
return idenxOf;
}
As usual late, but joining the party. A natural classic algorithm:
static int IndexOfLongestRun(string input)
{
int longestRunStart = -1, longestRunLength = 0;
for (int i = 0; i < input.Length; )
{
var runValue = input[i];
int runStart = i;
while (++i < input.Length && input[i] == runValue) { }
int runLength = i - runStart;
if (longestRunLength < runLength)
{
longestRunStart = runStart;
longestRunLength = runLength;
}
}
return longestRunStart;
}
At the end you have both longest run index and length.
public static int IndexOfLongestRun(string str)
{
var longestRunCount = 1;
var longestRunIndex = 0;
var isNew = false;
var dic = new Dictionary<int, int>();
for (var i = 0; i < str.Length - 1; i++)
{
if (str[i] == str[i + 1])
{
if (isNew) longestRunIndex = i;
longestRunCount++;
isNew = false;
}
else
{
isNew = true;
dic.Add(longestRunIndex, longestRunCount);
longestRunIndex = 0;
longestRunCount = 1;
}
}
return dic.OrderByDescending(x => x.Value).First().Key;
}
This will return -1 if the string is empty and you have the flexibility of returning the index and the count depending on your specification.
string myStr = "aaaabbbbccccccccccccdeeeeeeeee";
var longestIndexStart = -1;
var longestCount = 0;
var currentCount = 1;
var currentIndexStart = 0;
for (var idx = 1; idx < myStr.Length; idx++)
{
if (myStr[idx] == myStr[currentIndexStart])
currentCount++;
else
{
if (currentCount > longestCount)
{
longestIndexStart = currentIndexStart;
longestCount = currentCount;
}
currentIndexStart = idx;
currentCount = 1;
}
}
return longestIndexStart;
The accepted answer from Kvam works great for small strings, but as the length approaches 100,000 characters (and perhaps this isn't needed), its efficiency wains.
public static int IndexOfLongestRun(string str)
{
Dictionary<string, int> letterCount = new Dictionary<string, int>();
for (int i = 0; i < str.Length; i++)
{
string c = str.Substring(i, 1);
if (letterCount.ContainsKey(c))
letterCount[c]++;
else
letterCount.Add(c, 1);
}
return letterCount.Values.Max();
}
This solution is twice as fast as Kvam's with large strings. There are, perhaps, other optimizations.

forward and backward search algorithm on an array

I'm working on a simple algorithm problem for practice and i'm trying to figure out why on about 20 percent of test cases it fails. The problem is thus, given an array of ints find the average of all valid ints in the array.
An int is valid if
It is greater than or equal to -273
at least one of the previous two or next two ints are two points away from the current one
if the int is invalid it should not be included in calculating the average. Also, I don't believe the problem wants the solution to be cyclic (not sure though just thought about it while writing this so will try) i.e. if you are at the first int array[0], then there are no previous two ints as opposed to the last two being the previous two in a cyclic array.
my strategy is summed up in the code below:
public double averageTemperature(int[] measuredValues)
{
Queue<int> qLeft = new Queue<int>(2);
Queue<int> qRight = new Queue<int>(2);
double sum = 0d;
int cnt = 0;
for (int i = 0; i < measuredValues.Length; i++)
{
if (measuredValues[i] < -273)
continue;
if (qLeft.Count == 3)
qLeft.Dequeue();
for (int j = i + 1; j < measuredValues.Length; j++)
{
if (qRight.Count == 2)
{
break;
}
qRight.Enqueue(measuredValues[j]);
}
if (b(qLeft, qRight, measuredValues[i]) == true)
{
sum += measuredValues[i];
cnt++;
qLeft.Enqueue(measuredValues[i]);
}
qRight.Clear();
}
if (cnt > 0)
return sum / cnt;
return -300.0;
}
bool b(Queue<int> a, Queue<int> b, int c)
{
foreach (int q in a)
{
if (Math.Abs(q - c) <= 2)
return true;
}
foreach (int w in b)
{
if (Math.Abs(w - c) <= 2)
return true;
}
return false;
}
However, my strategy fails for this test case
{-13, 12, -14, 11, -15, 10, -16, 9, -17, 8, -18, 7, 6, -19, 5, -400, -400, 4, -390, -300, -270, 3, -12, 3, 2}
I don't understand why. I'm i missing something obvious? i know they're might be another more efficient way of solving this but i don't want to try them until i know why my "naive" way does not work.
Well I finally figured out why thanks to you guys. Here is my revised code for those who may find it helpful:
public double averageTemperature(int[] measuredValues)
{
Queue<int> qLeft = new Queue<int>(2);
Queue<int> qRight = new Queue<int>(2);
double sum = 0d;
int cnt = 0;
for (int i = 0; i < measuredValues.Length; i++)
{
if (qLeft.Count == 3)
qLeft.Dequeue();
for (int j = i + 1; j < measuredValues.Length; j++)
{
if (qRight.Count == 2)
{
break;
}
qRight.Enqueue(measuredValues[j]);
}
if (isValid(qLeft, qRight, measuredValues[i]) == true)
{
sum += measuredValues[i];
cnt++;
}
qLeft.Enqueue(measuredValues[i]);
qRight.Clear();
}
if (cnt > 0)
return sum / cnt;
return -300.0;
}
bool isValid(Queue<int> a, Queue<int> b, int c)
{
foreach (int q in a)
{
if (c >=-273 && Math.Abs(q - c) <= 2)
return true;
}
foreach (int w in b)
{
if (c >=-273 && Math.Abs(w - c) <= 2)
return true;
}
return false;
}
try starting at the same point in the nested for() loop when comparing. like this: what do you get when you run it?
public double averageTemperature(int[] measuredValues)
{
Queue<int> qLeft = new Queue<int>(2);
Queue<int> qRight = new Queue<int>(2);
double sum = 0d;
int cnt = 0;
for (int i = 0; i < measuredValues.Length; i++)
{
if (measuredValues[i] < -273)
continue;
if (qLeft.Count == 3)
qLeft.Dequeue();
for (int j = 0; j < measuredValues.Length; j++)
{
if (qRight.Count == 2)
{
break;
}
qRight.Enqueue(measuredValues[j]);
}
if (b(qLeft, qRight, measuredValues[i]) == true)
{
sum += measuredValues[i];
cnt++;
qLeft.Enqueue(measuredValues[i]);
}
qRight.Clear();
}
if (cnt > 0)
return sum / cnt;
return -300.0;
}
bool b(Queue<int> a, Queue<int> b, int c)
{
foreach (int q in a)
{
if (Math.Abs(q - c) <= 2)
return true;
}
foreach (int w in b)
{
if (Math.Abs(w - c) <= 2)
return true;
}
return false;
}
is it adding one each direction to put you two away like you were before?
You are enqueuing into qLeft only when the current value in the array is valid, but this is not right. You need to enqueue into qLeft at each iteration of the outer for-loop that is being controlled by i.
See the following code:
for (int i = 0; i < measuredValues.Length; i++)
{
if (measuredValues[i] < -273)
continue;
if (qLeft.Count == 3)
qLeft.Dequeue();
for (int j = i + 1; j < measuredValues.Length; j++)
{
if (qRight.Count == 2)
{
break;
}
qRight.Enqueue(measuredValues[j]);
}
if (b(qLeft, qRight, measuredValues[i]) == true)
{
sum += measuredValues[i];
cnt++;
}
qLeft.Enqueue(measuredValues[i]); // YOU NEED TO ENQUEUE INTO qLeft EACH TIME REGARDLESS OF IT IS VALID OR INVALID
qRight.Clear();
}

Search for an Array or List in a List

Have
List<byte> lbyte
Have
byte[] searchBytes
How can I search lbyte for not just a single byte but for the index of the searchBytes?
E.G.
Int32 index = lbyte.FirstIndexOf(searchBytes);
Here is the brute force I came up with.
Not the performance I am looking for.
public static Int32 ListIndexOfArray(List<byte> lb, byte[] sbs)
{
if (sbs == null) return -1;
if (sbs.Length == 0) return -1;
if (sbs.Length > 8) return -1;
if (sbs.Length == 1) return lb.FirstOrDefault(x => x == sbs[0]);
Int32 sbsLen = sbs.Length;
Int32 sbsCurMatch = 0;
for (int i = 0; i < lb.Count; i++)
{
if (lb[i] == sbs[sbsCurMatch])
{
sbsCurMatch++;
if (sbsCurMatch == sbsLen)
{
//int index = lb.FindIndex(e => sbs.All(f => f.Equals(e))); // fails to find a match
IndexOfArray = i - sbsLen + 1;
return;
}
}
else
{
sbsCurMatch = 0;
}
}
return -1;
}
Brute force is always an option. Although slow in comparison to some other methods, in practice it's usually not too bad. It's easy to implement and quite acceptable if lbyte isn't huge and doesn't have pathological data.
It's the same concept as brute force string searching.
You may find Boyer-Moore algorithm useful here. Convert your list to an array and search. The algorithm code is taken from this post.
static int SimpleBoyerMooreSearch(byte[] haystack, byte[] needle)
{
int[] lookup = new int[256];
for (int i = 0; i < lookup.Length; i++) { lookup[i] = needle.Length; }
for (int i = 0; i < needle.Length; i++)
{
lookup[needle[i]] = needle.Length - i - 1;
}
int index = needle.Length - 1;
var lastByte = needle.Last();
while (index < haystack.Length)
{
var checkByte = haystack[index];
if (haystack[index] == lastByte)
{
bool found = true;
for (int j = needle.Length - 2; j >= 0; j--)
{
if (haystack[index - needle.Length + j + 1] != needle[j])
{
found = false;
break;
}
}
if (found)
return index - needle.Length + 1;
else
index++;
}
else
{
index += lookup[checkByte];
}
}
return -1;
}
You can then search like this. If lbyte will remain constant after a certain time, you can just convert it to an array once and pass that.
//index is returned, or -1 if 'searchBytes' is not found
int startIndex = SimpleBoyerMooreSearch(lbyte.ToArray(), searchBytes);
Update based on comment. Here's the IList implementation which means that arrays and lists (and anything else that implements IList can be passed)
static int SimpleBoyerMooreSearch(IList<byte> haystack, IList<byte> needle)
{
int[] lookup = new int[256];
for (int i = 0; i < lookup.Length; i++) { lookup[i] = needle.Count; }
for (int i = 0; i < needle.Count; i++)
{
lookup[needle[i]] = needle.Count - i - 1;
}
int index = needle.Count - 1;
var lastByte = needle[index];
while (index < haystack.Count)
{
var checkByte = haystack[index];
if (haystack[index] == lastByte)
{
bool found = true;
for (int j = needle.Count - 2; j >= 0; j--)
{
if (haystack[index - needle.Count + j + 1] != needle[j])
{
found = false;
break;
}
}
if (found)
return index - needle.Count + 1;
else
index++;
}
else
{
index += lookup[checkByte];
}
}
return -1;
}
Since arrays and lists implement IList, there's no conversion necessary when calling it in your case.
int startIndex = SimpleBoyerMooreSearch(lbyte, searchBytes);
Another way you could do with lambda expression
int index = lbyte.FindIndex(e => searchBytes.All(i => i.Equals(e));

Categories