0

Given a string of word arrange them in such an order that there exist words like X -> Y -> Z in such a way that last char of X is first char of Y and last char of Y is first char of Z.

For example :

"sam let mat xaml tax"

will become

"sam mat tax xaml let"
6
  • Just use explode(' ', $string) Commented May 20, 2014 at 12:30
  • 3
    What if such an arrangement is not possilbe? (What will happen with "foo bar baz" for example? Commented May 20, 2014 at 12:30
  • He didn't say it can contain more or less than 5 words Commented May 20, 2014 at 12:31
  • @djay there was no mention of specific words amount in the input. the only condition is that last character in a word should be the first character in the next one Commented May 20, 2014 at 12:32
  • 1
    The general algorithm is pretty simple: pick any word, take its last letter, loop through the other words to find one which starts with that letter, put it in the list, rinse, repeat. It gets a lot trickier once you get into the edge cases like the above mentioned case where such a word may not exist. For that you'd have to define your situation and possible input better. Commented May 20, 2014 at 12:40

2 Answers 2

1

try recursion function:

<?php
$myStr = "sam let mat xaml tax";
var_dump($myStr);

$myStr = explode(' ', $myStr);
$finalArr = find($myStr[0], $myStr, array(), array());
$finalArr = array_unique($finalArr);

$strFinal = implode(" ", $finalArr);
var_dump($strFinal);

function find($currentWord, $myStr, $OUTPUT = array(), $unic = array()){
    $indexWord = array_search($currentWord, $myStr);
    $findFlag = false;
    foreach($myStr as $keyInLoop=>$wordInLoop){
        if(substr($currentWord, -1) == $wordInLoop[0]){
            $findFlag = true;
            $OUTPUT[] = $currentWord;
            $OUTPUT[] = $wordInLoop;
            unset($myStr[$indexWord]);
            $myStr = array_values($myStr);

            if(!count($myStr)) return array_merge($OUTPUT, $unic);
            elseif(count($myStr) == 1) {$unic[count($OUTPUT)+count($unic)+1] = $myStr[0]; return array_merge($OUTPUT, $unic);}
            else return find($wordInLoop, $myStr, $OUTPUT, $unic);
        }
    }
    if(!$findFlag){
        $unic[count($OUTPUT)+count($unic)+1] = $currentWord;
        unset($myStr[$indexWord]);
        $myStr = array_values($myStr);
        if(!count($myStr)) return array_merge($OUTPUT, $unic);
        elseif(count($myStr) == 1) {$unic[count($OUTPUT)+count($unic)+1] = $myStr[0]; return array_merge($OUTPUT, $unic);}
        else return find($myStr[0], $myStr, $OUTPUT, $unic);
    }
}
?>
Sign up to request clarification or add additional context in comments.

3 Comments

This is not working if you use the entry: $myStr = "sam let matz great xaml tax"; (add z to mat) Sorry
No, it working successfully but with "matz" there is no any word has prefix "z" so this function consider this word as unique (you can process unique word as you want) :)
Seen that way ;-) I didn't consider to restart the search for next word if it stopped finding. I consider the problem stopping from the moment you couldn't find another. I upvote.
1

My version of this algorithm rearranges the words, but: - does not include those words that cannot form the chain of words (the chain of words is started by the word that finds one coincidence at least) - does not restart another chain with the rest of words that didn't coincide with the first chain of words. That is to say, there is only 1 chain of words

That is to say that:

"ysamz let mat xaml gallery tax"

would give:

let tax xaml

this happens because it does not find any coincidence for ysamz, gallery and mat.

http://sandbox.onlinephpfunctions.com/code/7f911e410510980331627832889816997abe24db

 function right($string,$chars) 
 { 
  $vright = substr($string, strlen($string)-$chars,$chars); 
  return $vright; 
} 
function left($string,$chars){ 
    $vleft=substr($string, 0,$chars);
    return $vleft;
} 
function findone($str,&$strArray,$currentword)
{
  $chosen="";
  foreach ($strArray as $i => $strinarray) 
  {
      if(left($strinarray,1)==$str)
      {
          $chosen=$strinarray;
          unset($strArray[$i]);
          break;
      }
  }
  return $chosen;
}
function findonepattern($str,&$strArray,$currentword)
{
   $chosen="";
    $coincidences=preg_grep('/^'.$str.'.*$/', $strArray);
    if(sizeof($coincidences)>0)
  {
     $chosen=current($coincidences);
     $delWord=array_keys($strArray, $chosen);
      unset($strArray[$delWord[0]]);
      $strArray=array_values($strArray);

    }

 return $chosen;

}


function rearrange($lastvalue,&$strArray)
{
    global $finalArray,$globalcount;
    $last=right($lastvalue, 1);
  // $chosen=findonepattern($last,$strArray,$lastvalue);
 $chosen=findone($last,$strArray,$lastvalue);
    if(!empty($chosen))
    {
    //this line is commented because if there is more than one similar word
          //it would not consider the second one
          //  if(!in_array($chosen, $finalArray)) $finalArray[]=$chosen; 
        $finalArray[]=$chosen;   

       rearrange($chosen, $strArray);
   }
 if(sizeof($finalArray)==1 && $globalcount<sizeof($strArray)) 
  {
    $globalcount++;
    $finalArray[0]=$strArray[$globalcount];
     rearrange($finalArray[0], $strArray);
  }
    return $finalArray;
}
$globalcount=0;
    $thestring="sam let mat xaml tax";
    $strArray=explode(" ",$thestring);
    $strArray=array_filter($strArray);
    $finalArray=array();
    $finalArray[]=$strArray[0];
    unset($strArray[0]);
    $finalArray=rearrange($finalArray[0],$strArray);
    foreach($finalArray as $finalstr)
    {
     echo $finalstr." ";
    }

PREG PATTERN Instead of using the findone function that makes use of "left", you could use a pattern but there is a worse performance:

function findonepattern($str,&$strArray,$currentword)
{
   $chosen="";
    $coincidences=preg_grep('/^'.$str.'.*$/', $strArray);
    if(sizeof($coincidences)>0)
  {
     $chosen=current($coincidences);
     $delWord=array_keys($strArray, $chosen);
      unset($strArray[$delWord[0]]);
      $strArray=array_values($strArray);

    }

 return $chosen;

}

You can see the performance with 3000 words here:

http://sandbox.onlinephpfunctions.com/code/5ae84a2dadc8140eeb1e776d74059a204877bf0a

First solution: aprox. 0.3 seconds Second solution (preg): aprox 1 second

2 Comments

Hmmm I think your way is the best :)
I have updated the answer, now you can see it work with 3000 words, without running out of memory.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.