问题
I'm trying to write a function that takes a square grid of letters and given a word to find from a list of words, it searches for it horizontally, vertically or diagonally (also looking backwards in each case). I've tried writing this function in various ways with no success so was wondering if my general algorithm sounded ok and implementable.
Return co-ordinates for everywhere the first letter of the word occurs so something like
[row,col] = find(grid==words(2))
with words being the list of words and grid being the square matrix. So this would search withingrid
for the second word inwords
.For each occurrence of this letter move vertically, horizontally and diagonally in all directions for the length of the word and if last letter is that of the last letter of the word we are looking for store each character from first to the last as words in an array.
Compare each of these words to the word we are looking for and if there is a match draw a line.
Thoughts?
回答1:
Considering a character array and sub-strings to find along horizontal, vertical, and both diagonal directions:
A = char(randi(16,7,10)+'a'-1)
A =
ilhpcdchkl
ooaecloocd
kogajcdkpg
imlnnbiihf
bigoahciin
afjfjdhgmp
pejcdfnmke
% horizontal string in row 4, starting at col 5
cH = [4 5]; l = 4; % strings of length 4
sh = A(cH(1),cH(2)+(0:l-1))
sh =
nbii
% vertical string in col 6, starting at row 3
cV = [2 6];
sv = A(cV(1)+(0:l-1),cV(2)).' %'
sv =
lcbh
% diagonal (downward) string starting at row 3, col 7
cD = [3 7];
sd = A((cD(1)+(0:l-1))+size(A,1)*((cD(2)+(0:l-1))-1))
sd =
diip
% diagonal (upward) string starting at row 5, col 2
cU = [5 2]
su = A((cU(1)-(0:l-1))+size(A,1)*((cU(2)+(0:l-1))-1))
su =
ilac
Start with a function that can search the rows of a matrix for a string:
function ij = strsearch(A,s)
C = mat2cell(A,ones(size(A,1),1),size(A,2));
matches = strfind(C,s);
rows = find(~cellfun(@isempty,matches));
if ~isempty(rows),
cols = vertcat(matches{rows});
else
cols = [];
end
ij = [rows cols];
For example, this gives is the (row,column) location of the horizontal string sh
in the matrix A
:
>> ij = strsearch(A,sh)
ij =
4 5
That's great for horizontal strings, but what we want is the ability to search in all orientations and directions. We a new function, call it wordsearch
, that will output the following:
>> matches = wordsearch(A,sh)
matches =
start: [4 5]
orientation: 'horizontal'
direction: 0 % forward
>> matches = wordsearch(A,sv)
matches =
start: [2 6]
orientation: 'vertical'
direction: 0
>> matches = wordsearch(A,sd)
matches =
start: [3 7]
orientation: 'downward diagonal'
direction: 0
>> matches = wordsearch(A,su)
matches =
start: [5 2]
orientation: 'upward diagonal'
direction: 0
>> matches = wordsearch(A,fliplr(sh))
matches =
start: [4 8] % sh goes from column 5 to 8, in row 4
orientation: 'h'
direction: 1 % backward
To get this, we can build on strsearch
to search for horizontal and vertical occurrences by transposing the matrix. Backwards occurrences can be found by flipping the input string. To search diagonals, we can use arrayfun
and diag
to extract the diagonals and search in a similar manner.
The general search function:
function ij = wordsearcher(A,s,orientation,order)
s = s(:).'; %' ensure row vector
if order, s = fliplr(s); end
switch lower(orientation(1))
case 'h'
ij = strsearch(A,s);
if order && ~isempty(ij), ij(:,2) = ij(:,2) + numel(s) - 1; end
case 'v'
ij = fliplr(strsearch(A.',s)); %'
if order && ~isempty(ij), ij(:,1) = ij(:,1) + numel(s) - 1; end
case 'd' % down-right diagonals
Cdiags = arrayfun(@(k)diag(A,k).',-size(A,1)+1:size(A,2)-1,'uni',0); %'
matches = strfind(Cdiags,s);
k = find(~cellfun(@isempty,matches));
if isempty(k), ij=[]; return; end
row = (k<=size(A,1)) .* (size(A,1) - k) + [matches{k}];
col = ~(k<=size(A,1)) .* (k - size(A,1)) + [matches{k}];
ij = [row; col].'; %'
if order, ij = ij+numel(s)-1; end
case 'u' % up-right diagonals
Cdiags = arrayfun(@(k)diag(flipud(A),k).', ... %' flip A up-down
-size(A,1)+1:size(A,2)-1,'uni',0);
matches = strfind(Cdiags,s);
k = find(~cellfun(@isempty,matches));
if isempty(k), ij=[]; return; end
row = ~(k<=size(A,1)) .* (size(A,1) - k) + k - [matches{k}] + 1;
col = ~(k<=size(A,1)) .* (k - size(A,1)) + [matches{k}];
ij = [row; col].'; %'
if order, ij=bsxfun(@plus,ij,numel(s)*[-1 1]); end
otherwise
error('bad orientation')
end
Wrap that with loops to search in all orientations/directions to get the wordsearch
function:
function matches = wordsearch(A,s)
matches = struct('start',[],'orientation',[],'direction',[]);
n=1; o='hvdu';
ostr = {'horizontal','vertical','downward diagonal','upward diagonal'};
for id=0:1,
for io=1:numel(o),
ij = wordsearcher(A,s,o(io),id);
if ~isempty(ij),
matches(n).start = ij;
matches(n).orientation = ostr{io};
matches(n).direction = id;
n = n+1;
end
end
end
I hope this works.
来源:https://stackoverflow.com/questions/20530946/searching-for-a-word-in-a-grid