Searching for a word in a grid

浪子不回头ぞ 提交于 2019-12-11 05:48:21

问题


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 within grid for the second word in words.

  • 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!