123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- <?php
- /* Algorithm for finding (sub)palindromes in phrase
- Note: if there are spaces, they will be ignored
- and the string will be treated as if they weren't there.
- Also, all duplicates will not be included in result.
-
- Manacher's Algorithm used here.
- */
-
- mb_internal_encoding("UTF-8"); // UTF-8 support
-
- class Palindrome {
-
- public $subpalindromes;
- public $success;
- public $fail;
-
- function __construct() {
- $this->subpalindromes = function ($phrase) {
- $phrase = mb_strtolower($phrase); // mb_* functions used to support UTF-8 encoding.
- $phrase = mb_ereg_replace(" ", "", $phrase); // remove all spaces
-
-
- $manacher_subpalindromes = self::manacher($phrase); // run Manacher's algorithm to find odd and even subpalindromes
- $odd_subpalindromes = $manacher_subpalindromes[0];
- $even_subpalindromes = $manacher_subpalindromes[1];
-
- $palindromes = [];
- for ($i = 0; $i < mb_strlen($phrase); $i++) {
- if ($odd_subpalindromes[$i] > 1) { // ignore palindromes which consits of one character
- $palindromeLength = $odd_subpalindromes[$i]; // $odd_subpalindromes represents array of lengths of biggest palindromes, where index is the index of the character, which is the center of this palindrome
- while ($palindromeLength > 1) {
- $palindromes[] = mb_substr($phrase, $i - $palindromeLength + 1, $palindromeLength*2 - 1);
- $palindromeLength--;
- }
- }
-
- // same as previous with specific changes
- if ($even_subpalindromes[$i] > 0) {
- $palindromeLength = $even_subpalindromes[$i];
- while ($palindromeLength > 0) {
- $palindromes[] = mb_substr($phrase, $i - $palindromeLength, $palindromeLength*2);
- $palindromeLength--;
- }
- }
- }
- return array_values(array_unique($palindromes));
- };
-
- /* Used for formatting correct success message
- depending on the number of words found.
- */
- $this->success = function ($n) {
- $n_1 = $n % 100;
-
- if ($n_1 > 19) {
- $n_1 = $n_1 % 10;
- }
-
- $palindromeWordForm = "палиндром";
- $foundWordForm = "Найдено";
-
- if ($n_1 >= 2 && $n_1 <= 4) {
- $palindromeWordForm = "палиндрома";
- } else if ($n_1 == 1) {
- $palindromeWordForm = "палиндром";
- $foundWordForm = "Найден";
- } else {
- $palindromeWordForm = "палиндромов";
- }
-
- return $foundWordForm." ".strval($n)." ".$palindromeWordForm;
- };
-
- $this->fail = "Не найдено ни одного палиндрома";
- }
-
-
-
- /* Manacher's algorithm.
- Time complexity: O(n)
- Space complexity: O(1)
- */
- private static function manacher($str) {
- $n = mb_strlen($str);
- $odd = array_fill(0, $n, 0); // odd subpalindromes
- $even = array_fill(0, $n, 0); // even subpalindromes
-
- /* Odd subpalindromes */
- $l = 0; // left edge of current rightest palindrome
- $r = -1; // right edge of current rightst palindrome
- for ($i = 0; $i < $n; $i++) {
- $k = $i > $r ? 1 : min($odd[$l + $r - $i], $r - $i + 1); // k - known via previous steps palindrome center offset (guaranteed biggest palindrome with center in current position [i-k, i+k]
- while ($i + $k < $n && $i - $k >= 0 && mb_substr($str, $i - $k, 1) == mb_substr($str, $i + $k, 1)) {
- $k++; // increment offset if we are still between 0 and string length and mirrored elements are equal
- }
- $odd[$i] = $k; // here we know max palindrome size with center in current position (i)
- if ($i + $k - 1 > $r) { // if right edge of current palindrome is righter than right edge of the previous rightest palindrome, renew l and r
- $l = $i - $k + 1;
- $r = $i + $k - 1;
- }
- }
-
- /* Even subpalindromes
- a bit modified previous algorithm (for odd subpalindromes)
- */
- $l = 0;
- $r = -1;
- for ($i = 0; $i < $n; $i++) {
- $k = $i > $r ? 0 : min($even[$l + $r - $i + 1], $r - $i + 1);
- while ($i + $k < $n && $i - $k - 1 >= 0 && mb_substr($str, $i + $k, 1) == mb_substr($str, $i - $k - 1, 1)) {
- $k++;
- }
- $even[$i] = $k;
- if ($i + $k - 1 > $r) {
- $l = $i - $k;
- $r = $i + $k - 1;
- }
- }
-
- return [$odd, $even];
- }
- }
|