Following this tutorial I met the Trie data structure. Since recently I've been programming in PHP I tried to solve the lecture's problem with that. I was able to achieve correct answers, but only for smaller inputs (Input #10 is a 2,82 MB file). Obviously, my algorithm is not scaling well. It also exceeds the default 128 MB memory limit of PHP.
My algorithm
There is a root node stored in Trie. Every node has a "children" member. I use the standard PHP array to store the children. A child key represents a character (currently I am creating a new node for every character, a-z lowercase, mapping to 0-25), a child value is a reference to another node.
The "weight" member that every nodes has is there because of the problem.
I would like to optimize my code, (or even rewrite it from stratch using a different approach) so that it can pass the tests for big inputs.
I'm interested in a solution to make this data structure work in PHP with big inputs, if possible.
My code
TrieNode class stores the tree hierarchy.
class TrieNode {
// weight is needed for the given problem
public $weight;
/* TrieNode children,
* e.g. [0 => (TrieNode object1), 2 => (TrieNode object2)]
* where 0 stands for 'a', 1 for 'c'
* and TrieNode objects are references to other TrieNodes.
*/
private $children;
function __construct($weight, $children) {
$this->weight = $weight;
$this->children = $children;
}
/** map lower case english letters to 0-25 */
static function getAsciiValue($char) {
return intval(ord($char)) - intval(ord('a'));
}
function addChild($char, $node) {
if (!isset($this->children)) {
$this->children = [];
}
$this->children[self::getAsciiValue($char)] = $node;
}
function isChild($char) {
return isset($this->children[self::getAsciiValue($char)]);
}
function getChild($char) {
return $this->children[self::getAsciiValue($char)];
}
function isLeaf() {
return empty($this->children);
}
}
Trie class stores the root TrieNode. It can insert and query nodes.
class Trie {
/* root TrieNode stores the first characters */
private $root;
function __construct() {
$this->root = new TrieNode(-1, []);
}
function insert($string, $weight) {
$currentNode = $this->root;
$l = strlen($string);
for ($i = 0; $i < $l; $i++) {
$char = $string[$i];
if(!$currentNode->isChild($char)) {
$n = new TrieNode($weight, null);
$currentNode->addChild($char, $n);
}
$currentNode->weight = max($weight, $currentNode->weight);
$currentNode = $currentNode->getChild($char);
}
}
function getNode($string) {
$currentNode = $this->root;
$l = strlen($string);
for ($i = 0; $i < $l; $i++) {
$char = $string[$i];
if ($currentNode->isLeaf() || !$currentNode->isChild($char)) {
return null;
}
$currentNode = $currentNode->getChild($char);
}
return $currentNode;
}
function getWeight($string) {
$node = $this->getNode($string);
return is_null($node) ? -1 : $node->weight;
}
}
Test code. Parses input and calls the Trie object.
//MAIN / TEST
/*
In case the problem page is down:
e.g.
INPUT
2 1
hackerearth 10
hackerrank 9
hacker
OUTPUT
10
where 2 is the number of inserts, 1 is the number of queries
"string number" is the string to insert and its "weight"
"hacker" is the string to query
10 is maximum the weight of the queried string (hacker -> 10)
*/
$trie = new Trie();
$handle = fopen('test.txt', 'r');
//$handle = STDIN; // <- this is for the online judge
list($n, $q) = fscanf($handle, "%d %d");
for ($i = 0; $i < $n; $i++) { // insert data
list($s, $weight) = fscanf($handle, "%s %d");
$trie->insert($s, $weight);
}
for ($i = 0; $i < $q; $i++) { // query data
$query = trim(strval(fgets($handle)));
echo $trie->getWeight($query) . PHP_EOL;
}
fclose($handle);
Fail
After making some tweaks and modifications, I have been able to get this thing to work for all the test cases except one test case is timing out,
Here is the whole code that will run successfully for all the test cases except the test case 10.
class TrieNode {
// weight is needed for the given problem
public $weight;
/* TrieNode children,
* e.g. [0 => (TrieNode object1), 2 => (TrieNode object2)]
* where 0 stands for 'a', 1 for 'c'
* and TrieNode objects are references to other TrieNodes.
*/
private $children;
function __construct($weight, $children) {
$this->weight = $weight;
$this->children = $children;
}
/** map lower case english letters to 0-25 */
static function getAsciiValue($char) {
return intval(ord($char)) - intval(ord('a'));
}
function addChild($char, $node) {
if (!isset($this->children)) {
$this->children = [];
}
$this->children[self::getAsciiValue($char)] = $node;
}
function isChild($char) {
return isset($this->children[self::getAsciiValue($char)]);
}
function getChild($char) {
return $this->children[self::getAsciiValue($char)];
}
function isLeaf() {
return empty($this->children);
}
}
class Trie {
/* root TrieNode stores the first characters */
private $root;
function __construct() {
$this->root = new TrieNode(-1, []);
}
function insert($string, $weight) {
$currentNode = $this->root;
$l = strlen($string);
for ($i = 0; $i < $l; $i++) {
$char = $string[$i];
if(!$currentNode->isChild($char)) {
$n = new TrieNode($weight, null);
$currentNode->addChild($char, $n);
}
$currentNode->weight = max($weight, $currentNode->weight);
$currentNode = $currentNode->getChild($char);
}
}
function getNode($string) {
$currentNode = $this->root;
if (empty($currentNode) || !isset($currentNode)) {
return null;
}
$l = strlen($string);
for ($i = 0; $i < $l; $i++) {
$char = $string[$i];
if (empty($currentNode) || $currentNode->isLeaf() || !$currentNode->isChild($char)) {
return null;
}
$currentNode = $currentNode->getChild($char);
if (empty($currentNode)) {
return null;
}
}
return $currentNode;
}
function getWeight($string) {
$node = $this->getNode($string);
return is_null($node) ? -1 : $node->weight;
}
}
$trie = new Trie();
//$handle = fopen('test.txt', 'r');
$handle = STDIN; // <- this is for the online judge
list($n, $q) = fscanf($handle, "%d %d");
for ($i = 0; $i < $n; $i++) { // insert data
list($s, $weight) = fscanf($handle, "%s %d");
$trie->insert($s, $weight);
}
for ($i = 0; $i < $q; $i++) { // query data
$query = trim(strval(fgets($handle)));
echo $trie->getWeight($query) . PHP_EOL;
}
fclose($handle);
I will try to add some more checks so that I could reduce the computation cycles taken by this program.
Below is the code with following optimizations -
Removed all unnecessary condition checks like
No need to check is node is a leaf because if node does not have a child a specified char then it does not matter if it is a leaf or not.
No need to check if {children} is initialized every time you add a child node. Removed this check initialized {children} to empty array in constructor itself.
Removed function to {getAsciiValue} instead using a simple associative array as. Also changing a {char} to ascii value has been moved from TrieNode to Trie class so that we don't need to convert it multiple times
After these optimization i came up following minimal solution, but this also can not pass the test #10. After reading about array in PHP I got to know that PHP does not implement array like other compiled languages, instead any array in PHP is just an ordered hash map and because of this array does not support constant time operations. https://stackoverflow.com/a/4904071/8203131
Also using SplFixedArray but did not help because it is an object and has cost of instantiation. It could have helped if tried using a large array to store whole Trie. You can try implementing a solution to using SplFixedArray to store whole Trie and check if you can get it to pass test #10.
<?php
/*
* Read input from stdin and provide input before running code
fscanf(STDIN, "%s\n", $name);
echo "Hi, ".$name;
*/
class TrieNode {
// weight is needed for the given problem
public $weight;
/* TrieNode children,
* e.g. [0 => (TrieNode object1), 2 => (TrieNode object2)]
* where 0 stands for 'a', 2 for 'c'
* and TrieNode objects are references to other TrieNodes.
*/
private $children;
function __construct($weight) {
$this->weight = $weight;
$this->children = [];
}
function addChild($char, $node) {
$this->children[$char] = $node;
}
function isChild($char) {
return isset($this->children[$char]);
}
function getChild($char) {
return $this->children[$char];
}
}
class Trie {
/* root TrieNode stores the first characters */
private $root;
function __construct() {
$this->root = new TrieNode(-1);
}
static $asciiValues = array(
"a" => 0,
"b" => 1,
"c" => 2,
"d" => 3,
"e" => 4,
"f" => 5,
"g" => 6,
"h" => 7,
"i" => 8,
"j" => 9,
"k" => 10,
"l" => 11,
"m" => 12,
"n" => 13,
"o" => 14,
"p" => 15,
"q" => 16,
"r" => 17,
"s" => 18,
"t" => 19,
"u" => 20,
"v" => 21,
"w" => 22,
"x" => 23,
"y" => 24,
"z" => 25
);
function insert($string, $weight) {
$currentNode = $this->root;
$l = strlen($string);
for ($i = 0; $i < $l; $i++) {
$char = self::$asciiValues[$string[$i]];
$currentNode->weight = max($weight, $currentNode->weight);
if($currentNode->isChild($char)) {
$childNode = $currentNode->getChild($char);
} else {
$childNode = new TrieNode($weight);
$currentNode->addChild($char, $childNode);
}
$currentNode = $childNode;
}
}
function getNodeWeight($string) {
$currentNode = $this->root;
$l = strlen($string);
for ($i = 0; $i < $l; $i++) {
$char = self::$asciiValues[$string[$i]];
if (!$currentNode->isChild($char)) {
return -1;
}
$currentNode = $currentNode->getChild($char);
}
return $currentNode->weight;
}
}
$trie = new Trie();
//$handle = fopen('test.txt', 'r');
$handle = STDIN; // <- this is for the online judge
list($n, $q) = fscanf($handle, "%d %d");
for ($i = 0; $i < $n; $i++) { // insert data
list($s, $weight) = fscanf($handle, "%s %d");
$trie->insert($s, $weight);
}
for ($i = 0; $i < $q; $i++) { // query data
//$query = trim(strval(fgets($handle)));
$query = trim(strval(fgets($handle)));
echo $trie->getNodeWeight($query) . PHP_EOL;
}
fclose($handle);
?>
Related
A PHP 7 compatibility check pointed out two errors in some PHP 5 code that is not compatible with PHP 7.
'clone' is a reserved keyword introduced in PHP version 5.0 and
cannot be invoked as a function (T_CLONE)
How can the following code be fixed so it is compatible with PHP 7?
/**
* Implements {#link aCssMinifierFilter::filter()}.
*
* #param array $tokens Array of objects of type aCssToken
* #return integer Count of added, changed or removed tokens; a return value larger than 0 will rebuild the array
*/
public function apply(array &$tokens)
{
$r = 0;
$transformations = array("-moz-keyframes", "-webkit-keyframes");
for ($i = 0, $l = count($tokens); $i < $l; $i++)
{
if (get_class($tokens[$i]) === "CssAtKeyframesStartToken")
{
for ($ii = $i; $ii < $l; $ii++)
{
if (get_class($tokens[$ii]) === "CssAtKeyframesEndToken")
{
break;
}
}
if (get_class($tokens[$ii]) === "CssAtKeyframesEndToken")
{
$add = array();
$source = array();
for ($iii = $i; $iii <= $ii; $iii++)
{
$source[] = clone($tokens[$iii]);
}
foreach ($transformations as $transformation)
{
$t = array();
foreach ($source as $token)
{
$t[] = clone($token);
}
$t[0]->AtRuleName = $transformation;
$add = array_merge($add, $t);
}
if (isset($this->configuration["RemoveSource"]) && $this->configuration["RemoveSource"] === true)
{
array_splice($tokens, $i, $ii - $i + 1, $add);
}
else
{
array_splice($tokens, $ii + 1, 0, $add);
}
$l = count($tokens);
$i = $ii + count($add);
$r += count($add);
}
}
}
return $r;
}
}
So, it looks like the code is trying to clone an object. But, you have parentheses around the method.
The code should read
$t[] = clone $token;
clone isn't a function per se, it's a function that calls an object's __clone method
I'm using GMP library of php to solve a problem of formulary.
public function gmp_sum($aRessource)
{
// Avec le while
$i = 0;
$nb_ressource = count($aRessource);
while ($i < $nb_ressource)
{
if ($i == 0)
{
$tmp = gmp_init($aRessource[$i]);
}
else
{
$tmp = gmp_add(gmp_init($aRessource[$i]),$tmp);
}
$i++;
}
return $tmp;
}
The variable $aRessource is equal to : array(1,2,4,8);
so my function gmp_sum is returning 15.
I want to create an algorithm who does the reverse operation, the function take the integer 15 and return me an array who contains 1 2 4 8. But I do not know where to start.
Thanks for the help
Solution :
Decompose integer to power of 2 in php
public function gmp_reverse($gmp_sum)
{
$res = array();
$i = 1;
while ($i < 64) // 64 bytes
{
$tmp = $gmp_sum & $i; // check if bytes equal to 1
if ($tmp != 0)
{
array_push($res,$i);
}
$i = $i * 2;
}
return $res;
}
Assuming you want an array which adds up to the sum, you want a reverse of that. This function assumes, you have a perfect input, so for example, 17 will not work.
Try it out.
function reversegen($gmpsum)
{
$stack = array();
$limit = $gmpsum;
$cur = 1;
for($sum = 0; $sum < $limit; )
{
echo $cur. "<br>";
array_push($stack,$cur);
$sum = $sum + $cur;
$cur = 2 * $cur;
}
return($stack);
}
$stack = reversegen(15);
print_r($stack);
15 above is for representative purpose. You can use, 31, 63, 127 etc and it will still work fine.
I have an $myArray with sub-arrays which always contain 5 numbers - the numbers are sorted by size and can not be repeated in the sub-array, but there can be more "identical" sub-arrays (sub-arrays with the same numbers) in $myArray.
$myArray = array(
array(1,2,3,4,5),
array(5,6,10,18,20),
array(1,2,3,4,5),
array(2,3,4,5,9),
array(1,2,3,7,9),
array(1,3,4,5,7),
array(2,3,4,7,9),
array(2,4,5,10,29),
array(1,8,10,11,15) // etc.
);
How can I get an combination (array) of $n numbers where this combination (or rather the 5-number combinations generated from this $n-number combination) will match the most of the sub-arrays of $myArray?
Example: the desired result for $n=7 for the $myArray would be array(1,2,3,4,5,7,9) because there are twenty one 5-number combinations in total derived from this result:
1,2,3,4,5
1,2,3,4,7
1,2,3,4,9
//... and so on
and these combinations would match almost all sub-arrays (only second and last two sub-arrays are out of range).
I have tried counting with array_count_values() but simple frequency of all numbers doesn't work in this case...
class Combinations implements Iterator
{
protected $c = null;
protected $s = null;
protected $n = 0;
protected $k = 0;
protected $pos = 0;
function __construct($s, $k) {
if(is_array($s)) {
$this->s = array_values($s);
$this->n = count($this->s);
} else {
$this->s = (string) $s;
$this->n = strlen($this->s);
}
$this->k = $k;
$this->rewind();
}
function key() {
return $this->pos;
}
function current() {
$r = array();
for($i = 0; $i < $this->k; $i++)
$r[] = $this->s[$this->c[$i]];
return is_array($this->s) ? $r : implode('', $r);
}
function next() {
if($this->_next())
$this->pos++;
else
$this->pos = -1;
}
function rewind() {
$this->c = range(0, $this->k);
$this->pos = 0;
}
function valid() {
return $this->pos >= 0;
}
//
protected function _next() {
$i = $this->k - 1;
while ($i >= 0 && $this->c[$i] == $this->n - $this->k + $i)
$i--;
if($i < 0)
return false;
$this->c[$i]++;
while($i++ < $this->k - 1)
$this->c[$i] = $this->c[$i - 1] + 1;
return true;
}
}
$tickets = array(
array(1,2,3,4,5),
array(5,6,10,18,20),
array(1,2,3,4,5),
array(2,3,4,5,9),
array(1,2,3,7,9),
array(1,3,4,5,7),
array(2,3,4,7,9),
array(2,4,5,10,29),
array(1,8,10,11,15) // etc.
);
// first we need to find all numbers that are actually in one of the arrays.
foreach($tickets as $anArray) {
foreach($anArray as $aNumberUsed){
$numbersUsed[$aNumberUsed] = $aNumberUsed;
}
}
// next we assign the number of integers in the set we are looking for.
$r = 7;
// next we run the above class on our array (which gets us all of the possible combinations of these numbers).
foreach(new Combinations($numbersUsed, 7) as $comboKey => $substring){
$comboList[$comboKey] = $substring;
$countWins = 0;
// here we loop through all of the 5 number arrays, and flag any array who has all the variables in this iteration of the possible numbers. There are cleaner ways to do this, but this is easy to understand.
foreach($tickets as $valueList) {
$countNumbersFound = 0;
foreach($valueList as $value) {
if(in_array($value, $substring)) {
$countNumbersFound++;
}
}
if($countNumbersFound == 5) {
$countWins++;
}
}
$foundCount[$comboKey] = $countWins;
}
$bigly = max($foundCount);
$key = array_search($bigly, $foundCount);
foreach($comboList[$key] as $wellDone) {
echo "$wellDone ,";
}
The class is blatantly stolen from here: http://www.developerfiles.com/combinations-in-php/
Everything after the class is original. I don't believe in reinventing the wheel.
Suppose i have a number let's say 5. Now lets assume that there are 5 members. Now each member started to count 1 to 2. Those member who get 2nd number leaves and then again count start from the next member. so at last in this scenario the 3rd member stays at last.
So i tried to implement like this. First of assign members as array $v.
for($i=1 ; $i<=5 ; $i++)
{
$v[] = $i;
}
$v1 = array_flip($v);
for($i=0 ; $i<=5 ; $i += 2 )
{
unset($v1[$i]);
}
echo "<pre>";
print_r($v1);
output
Array
(
[1] => 0
[3] => 2
[5] => 4
)
Now i want to count the numbers from key 5(5th member) to again 1(1st member) and so on.
so at last key 3(3rd member) left.
I want to print the last member that left.
How can i achieve this?
I you can't understand then look at this
Survival Strategy
Here's an Object-Oriented solution with an easy-to-follow reduce method and multiple examples.
class CountByTwoArrayReducer {
public function __construct($array) {
$this->array = $array;
$this->size = count($array);
}
public function reduce() {
$this->initialize();
while($this->hasMultipleItems()) {
$this->next();
$this->removeCurrentItem();
$this->next();
}
return $this->finalItem();
}
protected function initialize() {
$this->current = 1;
$this->removed = array();
$this->remaining = $this->size;
}
protected function hasMultipleItems() {
return ($this->remaining > 1);
}
protected function next($start = null) {
$next = ($start === null) ? $this->current : $start;
do {
$next++;
} while(isset($this->removed[$next]));
if($next > $this->size)
$this->next(0);
else
$this->current = $next;
}
protected function removeCurrentItem() {
$this->removed[$this->current] = 1;
$this->remaining--;
}
protected function finalItem() {
return $this->array[$this->current - 1];
}
}
$examples = array(
array('A', 'B', 'C', 'D', 'E'),
range(1, 100),
range(1, 1000),
range(1, 10000)
);
foreach($examples as $example) {
$start = microtime(true);
$reducer = new CountByTwoArrayReducer($example);
$result = $reducer->reduce();
$time = microtime(true) - $start;
echo "Found {$result} in {$time} seconds.\n";
}
This will remove every other item from the array until there is only a single item left.
$members = range(1, 5);
$i = 0;
while(count($members) > 1) {
$i++;
if($i == count($members)) $i = 0;
unset($members[$i]);
$members = array_values($members);
if($i == count($members)) $i = 0;
}
echo $members[0];
Hmm, I can recommend two functions:
http://php.net/manual/en/function.array-keys.php
This would re-index you array, with indexes: 0, 1, 2
http://php.net/manual/en/control-structures.foreach.php
With this you can go through any array by:
foreach($v1 as $key=>$value) { ...select maximum, etc. ... }
<?php
function build_thieves($thieves)
{
return range(1, $thieves);
}
function kill_thief(&$cave)
{
if(sizeof($cave)==1)
{
$thief=array_slice($cave, 0, 1);
echo $thief.' survived';
return false;
}
$thief=array_slice($cave, 0, 1);
array_push($cave, $thief);
$thief=array_slice($cave, 0, 1);
echo $thief.' killed';
return true;
}
$cave=build_thieves(5);
$got_data=true;
while($got_data)
{
$got_data=kill_thief($cave);
}
Adjusted to every 2nd, not every 3rd. And starting from 1 not 0
This answer is a bit more complex, but it is much more efficient. It doesn't create an array of items and then remove them. It starts with a value (e.g. 1) and calculates the next item that has not been removed yet. Then, it flags it as removed. If you actually have an array of items, the index of the final item will be $current - 1. The example below uses values 1 through 10,000. On my machine, it takes just over 0.05 seconds.
define('SIZE', 10000);
/**
* Helper function to return the next value
*/
function get_next($current, &$removed) {
$next = $current;
do {
$next++;
} while(isset($removed[$next]));
return ($next > SIZE) ? get_next(0, $removed) : $next;
}
$current = 1;
$removed = array();
$remaining = SIZE;
$start_time = microtime(true);
while($remaining > 1) {
$current = get_next($current, $removed);
$removed[$current] = 1;
$remaining = SIZE - count($removed);
$current = get_next($current, $removed);
}
$total_time = microtime(true) - $start_time;
echo "Processed " . SIZE . " items\n";
echo "Winning item: {$current}\n";
echo "Total time: {$total_time} seconds\n";
I need an algorithm that return all possible combination of all characters in one string.
I've tried:
$langd = strlen($input);
for($i = 0;$i < $langd; $i++){
$tempStrang = NULL;
$tempStrang .= substr($input, $i, 1);
for($j = $i+1, $k=0; $k < $langd; $k++, $j++){
if($j > $langd) $j = 0;
$tempStrang .= substr($input, $j, 1);
}
$myarray[] = $tempStrang;
}
But that only returns the same amount combination as the length of the string.
Say the $input = "hey", the result would be: hey, hye, eyh, ehy, yhe, yeh.
You can use a back tracking based approach to systematically generate all the permutations:
// function to generate and print all N! permutations of $str. (N = strlen($str)).
function permute($str,$i,$n) {
if ($i == $n)
print "$str\n";
else {
for ($j = $i; $j < $n; $j++) {
swap($str,$i,$j);
permute($str, $i+1, $n);
swap($str,$i,$j); // backtrack.
}
}
}
// function to swap the char at pos $i and $j of $str.
function swap(&$str,$i,$j) {
$temp = $str[$i];
$str[$i] = $str[$j];
$str[$j] = $temp;
}
$str = "hey";
permute($str,0,strlen($str)); // call the function.
Output:
#php a.php
hey
hye
ehy
eyh
yeh
yhe
My variant (works as well with array or string input)
function permute($arg) {
$array = is_string($arg) ? str_split($arg) : $arg;
if(1 === count($array))
return $array;
$result = array();
foreach($array as $key => $item)
foreach(permute(array_diff_key($array, array($key => $item))) as $p)
$result[] = $item . $p;
return $result;
}
P.S.: Downvoter, please explain your position. This code uses additional str_split and array_diff_key standard functions, but this code snippet is the smallest, it implements pure tail recursion with just one input parameter and it is isomorphic to the input data type.
Maybe it will lose benchmarks a little when comparing with other implementations (but performance is actually almost the same as in #codaddict's answer for several character strings), but why we can't we just consider it as one of the different alternatives which has its own advantages?
I would put all the characters in an array, and write a recursive function that will 'stripe out' all the remaining characters. If the array is empty, to a reference passed array.
<?php
$input = "hey";
function string_getpermutations($prefix, $characters, &$permutations)
{
if (count($characters) == 1)
$permutations[] = $prefix . array_pop($characters);
else
{
for ($i = 0; $i < count($characters); $i++)
{
$tmp = $characters;
unset($tmp[$i]);
string_getpermutations($prefix . $characters[$i], array_values($tmp), $permutations);
}
}
}
$characters = array();
for ($i = 0; $i < strlen($input); $i++)
$characters[] = $input[$i];
$permutations = array();
print_r($characters);
string_getpermutations("", $characters, $permutations);
print_r($permutations);
Prints out:
Array
(
[0] => h
[1] => e
[2] => y
)
Array
(
[0] => hey
[1] => hye
[2] => ehy
[3] => eyh
[4] => yhe
[5] => yeh
)
Ah yes,
combinations = order doens't matter.
permutations = order does matter.
So hey, hye yeh are all the same combination, but 3 separate permutations as mentioned. Watch out that the scale of items goes up very fast. It's called factorial, and is written like 6! = 6*5*4*3*2*1 = 720 items (for a 6 character string). A 10 character string will be 10! = 3628800 permutations already, which is a very big array. In this example it's 3! = 3*2*1 = 6.
My approach uses recursion and no loops, please check and give feedback:
function permute($str,$index=0,$count=0)
{
if($count == strlen($str)-$index)
return;
$str = rotate($str,$index);
if($index==strlen($str)-2)//reached to the end, print it
{
echo $str."<br> ";//or keep it in an array
}
permute($str,$index+1);//rotate its children
permute($str,$index,$count+1);//rotate itself
}
function rotate($str,$index)
{
$tmp = $str[$index];
$i=$index;
for($i=$index+1;$i<strlen($str);$i++)
{
$str[$i-1] = $str[$i];
}
$str[$i-1] = $tmp;
return $str;
}
permute("hey");
I made a simple class that uses Generators to create the permutations.This way you can just iterate over all possible combinations without exhausting the memory.
The class can take either a string or an array,
and returns a Generator object which can be iterated over with foreach.
Obviously the longer the string or array, the longer it takes to generate all the permutations.
This has been build against PHP 7.4
class Permutation {
/** #var string|array **/
protected $permutationRoot;
protected int $permutationLength;
/**
* #param $permutationRoot
*/
protected function __construct( $permutationRoot ) {
$this->permutationRoot = $permutationRoot;
$this->permutationLength = is_array($permutationRoot)
? count($permutationRoot)
: strlen($permutationRoot);
}
/**
* #param string|array $permutationRoot
*
* #return \Generator
*/
public static function resolve( $permutationRoot ): \Generator
{
$instance = new static($permutationRoot);
return $instance->backTrack(
$instance->permutationRoot,
0,
$instance->permutationLength,
);
}
/**
* #param string|array $permutation
* #param int $index
* #param int $length
*
* #return \Generator
*/
protected function backTrack($permutation, int $index, int $length): \Generator
{
if ($index === $length) {
yield $permutation;
}
for ($i = $index; $i < $length; $i++) {
$this->swap($permutation, $index, $i);
yield from $this->backTrack($permutation, $index + 1, $length);
$this->swap($permutation, $index, $i); // backtrack.
}
}
/**
* #param $permutation
* #param int $index
* #param int $n
*
* #return void
*/
protected function swap(&$permutation, int $index, int $n): void {
$temp = $permutation[$index];
$permutation[$index] = $permutation[$n];
$permutation[$n] = $temp;
}
}
// Test
foreach ( Permutation::resolve('hey') as $perm ) {
echo $perm . "\n";
}
$sentence = "This is a cat";
$words = explode(" ", $sentence);
$num_words = count($words);
$uniqueWords = [];
for ($i = 0; $i < $num_words; $i++) {
for ($j = $i; $j < $num_words; $j++) {
$uniqueWord = '';
for ($k = $i; $k <= $j; $k++) {
$uniqueWord .= $words[$k] . ' ';
}
$uniqueWords[] = trim($uniqueWord);
}
}
var_dump($uniqueWords);
This worked for me