Как написать палиндром

специально для gwean и mym_kak_mym
Лена набила рожу мужу — муж орал и банан ел. (
Михаил Фельдман)

1. Допустим, что исходное слово при построении палиндрома Лена.

2. Запишем его дважды одно под другим в две строки:

         Лена

         Лена

3. Первую строчку прочитаем как обычно — слева направо: Лена, а вторую в обратном порядке — справа налево (бустрофедон, «ходом быка», как читали древние греки): анел.

4. Соберем все в одно целое, читая теперь уже только слева направо в одну строчку: Ленаанел.

5. Если левая часть (аверс) — исходное слово — была осмысленна изначально: Лена, то правая (реверс) получилась явно бессмысленной: анел.

6. Попробуем выделить в реверсе осмысленные слова («материализовать» слова): -ан [ел].

7. Сборка: Лена— ан ел.

8. Согласовывая Субъект Лена и Предикат ел — получим: Ленаел(а).

9. Соответственно согласованная флексия симметрично породит союз А в начале Предложения в общем случае: А Лена-ан ел(а), или слово, начинающееся на А-, если повезет: Алена-ан ел(а)

10. Если исходить из семантической матрицы Предложения SVO: Субъект Предикат(глагол) Объект, то здесь заполнены только 2 места — SV, а место Объекта (Лена ела Что?) свободно.

11. Материализуем Объект: А ЛенаБАНан ел(а).

12. Тогда слева возникает паразитный морф НАБ-.

13. В нем сразу же материализуется [НА] = 1) предлог на или 2) префикс на-.

14. Далее возникает альтернатива: 1) остаться в пределах одного простого Предложения SVO, выбрав предлог на, задающий косвенный падеж периферийного участника, что-то типа А Лена НА БОКУ/ БАЗАРЕ /БИДЕ … БАНан ела (Ср.: А Лена на боку, суко!, банан ела!) или 2) выбрав префикс на-, ввести новый Предикат и тем самым перейти к сложному Предложению  SVO1- SVO2 или  хотя бы причастному обороту SVO1,VO2 (Ср.: А Лена, набрав чвар, банан ела!).

15. Если выбран второй Предикат наб-, первоначальное Предложение разрушается и возникает 2 новых: А Лена НАБИЛА … -АЛ И БАНан ел(а) с незаполненными ячейками семантической матрицы: SV?1… ?OV2.

16. Что же такое набила Лена? Первое, что приходит в голову — идиома Набила морду! (Ср.: Лена, набила рот — оря, рот орал — и банан ел!).

17. Поскольку ничего путного при обратном чтении не материализуется, попробуем синоним морду=рожу: Набила рожу!

18. Сборка: А Лена набила рожу… уж орал и банан ел(а).

19. Поскольку явно намечаются однородные предикаты орал и ел в левой части, проведем рассогласование — уберем флексию и соответственно уйдет союз А слева: Лена набила рожу… уж орал и банан ел.

20. В первом Предложении не хватает только Адресата (набила рожу Кому?). Если Субъектом второго Предложения считать ужа, то конечно — ужу! Тем более что это просто пробка в дырке челюстно-лицевого палиндрома.

21. Сборка: Лена набила рожу ужу, уж орал и банан ел.

22. Но тут есть противоречие: рожа — неприятное лицо, а лицо только у человека, а у ужа — морда, да и орать он может только чисто метонимически-метафорически (издавал звук), скорее — шипел, и не факт еще, что ужи питаются бананами! Да и как можно набить маленькую мордочку ужа,  да и чем? Тут налицо разные семантические миры!

23. Слава богу, что центр палиндрома можно заполнить добавлением одной согласной (материализовать слово Муж), и соответственно Адресатом будет Мужу, что сразу же исправляет семантический мир: у мужа и [жена] Лена, и рожа, которую легко набить, так, что он будет орать, да и банан — вполне его еда.

24. Сборка: Лена набила рожу мужу, муж орал — и банан ел.

25. А полная семантическая экспликация: [Жена] Лена набила рожу [своему]  мужу, [в результате чего] муж орал — и [потом] банан ел.

26. Если отключиться от конкретики, то в Предложении кроме Субъект-Предикат-Объектных отношений выражаются отношения Родства, Принадлежности, Причины-Следствия, Следования.

27. Т.о. вырисовываются семантические приемы построения палиндрома: бустрофедон, сборка, материализация слова, согласование, рассогласование, заполнение семантической матрицы простого предложения SVO, преобразование одной группы  SVO простого предложения в несколько групп SVO1,  SVO2 и т.д. сложного предложения, идиомы, синонимы, однородные члены, поиск периферийных участников при заполненной матрице SVO, однородный семантический мир, образность.

28. Но м.б. есть смысл идти от предикатов (глаголов)? Если здесь идти от фразы: набила рожу мужу, или хотя бы устойчивой синтагмы набила рожу, то это автоматически породит всю фразу: 1) набила рожу …; 2) набила рожу … Муж орал и бан-; 3) ЛЕНА набила рожу … Муж орал и банАН ЕЛ; 4) Лена набила рожу МУЖУ муж орал и банан ел.

Сайт переезжает. Большинство статей уже перенесено на новую версию.
Скоро добавим автоматические переходы, но пока обновленную версию этой статьи можно найти там.

Палиндромы

Палиндром — это фраза, которая читается одинаково слева направо и справа налево. Например:

  • «was it a car or a cat I saw?»

  • «а роза упала на лапу Азора»

  • «abacaba» (палиндром нечётной длины)

  • «abba» (палиндром чётной длины)

Для простоты, мы будем рассматривать только последовательности из строчных латинских букв.

Палиндромы — не самые часто встречающиеся в реальной жизни объекты, однако задачи на палиндромы любят давать на соревнованиях по спортивному программированию. В этой статье мы опишем эффективные способы их представления.

Алгоритм Манакера

Пусть есть строка (s) и мы хотим найти в ней все подпалиндромы.

Мы сразу сталкиваемся с очевидной трудностью: их в строке может быть (O(n^2)), что можно видеть на примере строки (s = aa ldots a). Поэтому будем использовать следующий формат: для каждой позиции (s_i) найдём наибольший палиндром, центр которого совпадает с (s_i) (чётные и нечётные палиндромы будем рассматривать отдельно). Половину его длины, округлённую вниз, будем называть радиусом.

Наивное решение — перебрать (s_i), а для него вторым циклом находить наибольшую искомую длину:

vector<int> pal_array(string s) {
    int n = s.size();

    // окружим строку спецсимволами, чтобы не рассматривать выход за границы
    s = "#" + s + "$";

    // в этом массиве будем хранить расстояние от центра до границы палиндрома
    vector<int> t(n, 0);

    for(int i = 1; i <= n; i++)
        while (s[i - t[i - 1]] == s[i + t[i - 1]])
            r[i-1]++;

    return r;
}

Тот же пример (s = aadots a) показывает, что данная реализация работает за (O(n^2)).

Для оптимизации применим идею, знакомую из алгоритма z-функции: при инициализации (t_i) будем пользоваться уже посчитанными (t). А именно, будем поддерживать ((l, r)) — интервал, соответствующий самому правому из найденных подпалиндромов. Тогда мы можем сказать, что часть наибольшего палиндрома с центром в (s_i), которая лежит внутри (s_{l:r}), имеет радиус хотя бы (min(r-i, ; t_{l+r-i})). Первая величина равна длине, дальше которой произошел бы выход за пределы (s_{l:r}), а вторая — значению радиуса в позиции, зеркальной относительно центра палиндрома (s_{l:r}).


vector<int> manacher_odd(string s) {
    int n = (int) s.size();
    vector<int> d(n, 1);
    int l = 0, r = 0;
    for (int i = 1; i < n; i++) {
        if (i < r)
            d[i] = min(r - i + 1, d[l + r - i]);
        while (i - d[i] >= 0 && i + d[i] < n && s[i - d[i]] == s[i + d[i]])
            d[i]++;
        if (i + d[i] - 1 > r)
            l = i - d[i] + 1, r = i + d[i] - 1;
    }
    return d;
}

Так же, как и z-функция, алгоритм работает за линейное время: цикл while запускается только когда (t_i = r — i) (иначе палиндром уже во что-то упёрся), и каждая его итерация сдвигает увеличивает (r) на единицу. Так как (r leq n), получаем, что суммарно эти циклы сделают (O(n)) итераций.

Для случая чётных палиндромов меняется только индексация:

vector<int> manacher_even(string s) {
    int n = (int) s.size();
    vector<int> d(n, 0);
    int l = -1, r = -1;
    for (int i = 0; i < n - 1; i++) {
        if (i < r)
            d[i] = min(r - i, d[l + r - i - 1]);
        while (i - d[i] >= 0 && i + d[i] + 1 < n && s[i - d[i]] == s[i + d[i] + 1])
            d[i]++;
        if (i + d[i] > r)
            l = i - d[i] + 1, r = i + d[i];
    }
    return d;
}

Также можно было не писать отдельно две реализации, а воспользоваться следующим трюком — сделать замену:

[
S = s_1 s_2 dots s_n to S^* = s_1 # s_2 # dots # s_n
]

Теперь нечётные палиндромы с центром в (s_i) соответствуют нечётным палиндромам исходной строки, а нечётные палиндромы с центром в (#) — чётным.

Дерево палиндромов

Дерево палиндромов (англ. palindromic tree, EERTREE) — структура данных, использующая другой, более мощный формат хранения информации обо всех подпалиндромах, чем размеры (n) палиндромов. Она была предложена Михаилом Рубинчиком на летних петрозаводских сборах в 2014-м году.

Лемма. В строке есть не более (n) различных подпалиндромов.

Доказательство. Пусть мы дописываем к строке по одному символу и в данный момент, записав (r) символов, имеем наибольший суффикс-палиндром (s_{l:r}). Пусть у него, в свою очередь, есть суффикс-палиндром (s_{l’:r} = t). Тогда он также имеет более раннее вхождение в строку как (s_{l:l+r-l’} = t). Таким образом, с каждым новым символом у строки появляется не более одного нового палиндрома, и если таковой есть, то это всегда наибольший суффикс-палиндром.

Этот факт позволяет сопоставить всем палиндромам строки сопоставить следующую структуру: возьмём от каждого палиндрома его правую половину (например, (caba) для (abacaba) или (ba) для (abba); будем рассматривать пока что только чётные палиндромы) и добавим все эти половины в префиксное дерево — получившуюся структуру и будем называть деревом палиндромов.

Наивный алгоритм построения будет в худшем случае работать за (O(n^2)), но это можно делать и более эффективно.

Построение за линейное время

Будем поддерживать наибольший суффикс-палиндром. Когда мы будем дописывать очередной символ (c), нужно найти наибольший суффикс этого палиндрома, который может быть дополнен символом (c) — это и будет новый наидлиннейший суффикс-палиндром.

Для этого поступим аналогично алгоритму Ахо-Корасик: будем поддерживать для каждого палиндрома суффиксную ссылку (l(v)), ведущую из (v) в её наибольший суффикс-палиндром. При добавлении очередного символа, будем подниматься по суффиксным ссылкам, пока не найдём вершину, из которой можно совершить нужный переход.

Если в подходящей вершине этого перехода не существовало, то нужно создать новую вершину, и для неё тоже понадобится своя суффиксная ссылка. Чтобы найти её, будем продолжать подниматься по суффиксным ссылкам предыдущего суффикс-палиндрома, пока не найдём второе такое место, которое мы можем дополнить символом (c).

const int maxn = 1e5, k = 26;

int s[maxn], len[maxn], link[maxn], to[maxn][k];

int n, last, sz;

void init() {
    s[n++] = -1;
    link[0] = 1;
    len[1] = -1;
    sz = 2;
}

int get_link(int v) {
    while (s[n-len[v]-2] != s[n-1])
        v = link[v];
    return v;
}

void add_char(int c) {
    s[n++] = c;
    last = get_link(last);
    if (!to[last][c]) {
        len[sz] = len[last] + 2;
        link[sz] = to[get_link(link[last])][c];
        to[last][c] = sz++;
    }
    last = to[last][c];
}

Здесь мы использовали обычный массив для хранения переходов. Как и для любых префиксных деревьев, вместо него можно использовтать бинарное дерево поиска, хэш-таблицу, односвязный список и другие структуры, позволяющие обменять время на память, немного изменив асимптотику.

Асимптотика

Покажем линейность алгоритма. Рассмотрим длину наибольшего суффикс-палиндрома строки. Каждый новый символ увеличивает её не более, чем на 2. При этом каждый переход по суффиксной ссылке уменьшает её, поэтому нахождение первого суффикс-палиндрома амортизировано работает за линейное время.

Аналогичными рассуждениями о длине второго суффикс-палиндрома (его длина увеличивается тоже не более, чем на 2) получаем, что пересчёт суффиксных ссылок при создании новых вершин тоже суммарно работает за линейное время.

The most important thing to do when solving a Technical Test is Don’t use shortcut methodsthey want to see how you think algorithmically! Not your use of methods.

Here is one that I came up with (45 minutes after I blew the test). There are a couple optimizations to make though. When writing any algorithm, its best to assume false and alter the logic if its looking to be true.

isPalindrome():

Basically, to make this run in O(N) (linear) complexity you want to have 2 iterators whose vectors point towards each other. Meaning, one iterator that starts at the beginning and one that starts at the end, each traveling inward. You could have the iterators traverse the whole array and use a condition to break/return once they meet in the middle, but it may save some work to only give each iterator a half-length by default.

for loops seem to force the use of more checks, so I used while loops — which I’m less comfortable with.

Here’s the code:

/**
 * TODO: If func counts out, let it return 0
 *  * Assume !isPalindrome (invert logic)
 */
function isPalindrome(S){
    var s = S
      , len = s.length
      , mid = len/2;
      , i = 0, j = len-1;

    while(i<mid){
        var l = s.charAt(i);
        while(j>=mid){
            var r = s.charAt(j);
            if(l === r){
                console.log('@while *', i, l, '...', j, r);
                --j;
                break;
            }
            console.log('@while !', i, l, '...', j, r);
            return 0;
        }
        ++i;
    }
    return 1;
}

var nooe = solution('neveroddoreven');  // even char length
var kayak = solution('kayak');  // odd char length
var kayaks = solution('kayaks');

console.log('@isPalindrome', nooe, kayak, kayaks);

Notice that if the loops count out, it returns true. All the logic should be inverted so that it by default returns false. I also used one short cut method String.prototype.charAt(n), but I felt OK with this as every language natively supports this method.

The most important thing to do when solving a Technical Test is Don’t use shortcut methodsthey want to see how you think algorithmically! Not your use of methods.

Here is one that I came up with (45 minutes after I blew the test). There are a couple optimizations to make though. When writing any algorithm, its best to assume false and alter the logic if its looking to be true.

isPalindrome():

Basically, to make this run in O(N) (linear) complexity you want to have 2 iterators whose vectors point towards each other. Meaning, one iterator that starts at the beginning and one that starts at the end, each traveling inward. You could have the iterators traverse the whole array and use a condition to break/return once they meet in the middle, but it may save some work to only give each iterator a half-length by default.

for loops seem to force the use of more checks, so I used while loops — which I’m less comfortable with.

Here’s the code:

/**
 * TODO: If func counts out, let it return 0
 *  * Assume !isPalindrome (invert logic)
 */
function isPalindrome(S){
    var s = S
      , len = s.length
      , mid = len/2;
      , i = 0, j = len-1;

    while(i<mid){
        var l = s.charAt(i);
        while(j>=mid){
            var r = s.charAt(j);
            if(l === r){
                console.log('@while *', i, l, '...', j, r);
                --j;
                break;
            }
            console.log('@while !', i, l, '...', j, r);
            return 0;
        }
        ++i;
    }
    return 1;
}

var nooe = solution('neveroddoreven');  // even char length
var kayak = solution('kayak');  // odd char length
var kayaks = solution('kayaks');

console.log('@isPalindrome', nooe, kayak, kayaks);

Notice that if the loops count out, it returns true. All the logic should be inverted so that it by default returns false. I also used one short cut method String.prototype.charAt(n), but I felt OK with this as every language natively supports this method.

Время на прочтение
9 мин

Количество просмотров 7.4K

PalidromePolyglotQuine

Поздравляю всех трансляторов человеческого языка в машинный с их профессиональным днем, желаю вам меньше багов и больше-либо-равно классных идей! А в качестве идейного подарка со своей стороны предлагаю решение одной красивой задачи — написание кода, который на выходе выдаёт свой собственный текст, является валидным для интерпретаторов и компиляторов разных языков, и при этом правильно исполняется при реверсе исходников.

Не так давно я узнал о коде, который может одновременно интерпретироваться в PHP и компилироваться в Java: PhpJava.java. Как оказалось, эта идея не нова: код, валидный сразу для нескольких компиляторов или интерпретаторов, называется полиглотом (polyglot). Такой код возможно писать из-за особенностей обработки строк и комментариев в различных интерпретаторах или компиляторах.

Например, в Java можно описывать символы как в обычном виде, например символ ‘/’, так и в виде юникод-записи: ‘u002F’. В компиляторе C# такие записи не будут валидными. Однако если их «спрятать» в комментарий, то, с одной стороны, они не будут мешать при компиляции C# кода, с другой — будут валидными в Java-коде. Например, если нужно, чтобы код A компилировался только в языке C#, но не компилировался в Java, а код B компилировался только в Java, но не компилировался в C#, нужно использовать следующий фрагмент:

//u000Au002Fu002A
A//u002Au002FB

C#-компилятор будет «воспринимать» этот код обычным образом, комментарии останутся комментариями:

//u000Au002Fu002A
A//u002Au002FB

А вот с Java все интересней, т.к. юникод-запись трасформируется и получится следующее:

//
/*
A//*/B

Комбинируя общие для обоих языков инструкции, а также разделяя различные, можно написать любую программу.

Однако такими юникод-записями трюки не ограничиваются. Например, в C-подобных языках существуют директивы препроцессора, которые могут определять, какой код должен компилироваться, а какой нет. В некомпилируемый код можно помещать код совсем другого языка. Например, ниже приведен код-полиглот на C++ и Python (отсюда), в котором в секции #if false как раз и помещен код Python. А последовательности ''' начинают и заканчивают комментарий в коде Python.

#include <stdio.h>
#if false
print "Hello world"
'''
#endif
int main() {
printf("Hello world");
return 0;
}
#if false
'''
#endif

В HTML, например, комментарии начинаются с последовательности символов <!--, что также можно использовать. Есть и более сложные полиглоты, работающие одновременно на 6 и на 16 языках. Первый выводит сообщение «hitforum» для всех языков, а второй работает интересней: выводит название языка, на котором происходит его компиляция или интерпретация.

Код-полиглот на C, Shell, Perl, Brainfuck, Befunge, Whitespace

# define x u    /*            v
#    :::::::::::::::::::>>>>>>>$$$a"muroftih"#[>:#,_@]
eval 'echo "hitforum";exit';sub echo { print    "@_n"}               
__END__>++++++++++>++++++++++[>+++++++++++>++++++++++    
+<<-]>------.+.>++++++.<---.+++++++++.>--.+++                        
.<--.<<.    */
main() { printf ("hitforumn"); }

Код-полиглот на 16 язках

# /* [  <!-- */ include <stdio.h> /*    
    #`{{coding=utf-8
"true" if 0 != 0 and    q != """0"  ;   `   
    
if [ -n "$ZSH_VERSION" ]; then                  
    
    echo exec   echo I'm a zsh script.; 
    
elif [ -n "$BASH_VERSION" ]; then               
    
    echo exec   echo I'm a bash script.; 
else    
    echo exec   echo    I'm    a sh    script.;        
fi`;    #!;#
BEGIN{print"I'm a ", 0 ? "Ruby" :"Perl",    " program.n";  exit; }  
    #
%q~                     

set =dummy 0; puts [list "I'm"  "a" "tcl"   "script."]; exit      

all: ; @echo "I'm a Makefile."              
    #*/
/*: */ enum {a, b};                     
    
static int c99(void) {                  

 #ifndef __cplusplus /* bah */              

unused1: if ((enum {b, a})0)                
    (void)0;
 #endif                 

unused2:    return a;        
}   
static int trigraphs(void) {                
    
    return sizeof   "??!"   ==  2;       
}   
char X;                         
    
int main(void) {                    
    
    struct X    {            
    
        char    a[2];       
    };
    if (sizeof(X)   !=  1) {            
    
printf("I'm a C++ program (trigraphs %sabled).n",               
    
     trigraphs()    ? "en"  : "dis");
    
}else if (1//**/2

)unused3 : { ; 
        printf("I'm a C program (C%s, trigraphs %sabled).n", 
               c99() ? "89 with // comments" : "99", 
               trigraphs() ? "en" : "dis"); 
    } else { 
        printf("I'm a C program (C89, trigraphs %sabled).n", 
               trigraphs() ? "en" : "dis"); 
    } 
    return 0; 
} /*
 # 
begin{code}
import Prelude hiding ((:)); import Data.List (intercalate); import Language.Haskell.TH; import Data.String; default (S, String, Integer, Double); data S = S; instance Eq S where { _ == _ = False }; instance IsString S where { fromString = const S }; ifThenElse c t e = case c of True -> t; False -> e
cPP = False; {-
#define cPP True
-} main :: IO ()
main = putStr ("I'm a Literate Haskell program" ++ bonus ++ ".n") where
  _ = (); bonus | null details = "" | otherwise = " (" ++ details ++ ")"
  details = intercalate ", " [ name | (True, name) <- extensions ] :: String
  extensions =
    (bangPatterns,              "BangPatterns"             ) :
    (templateHaskell,           "TemplateHaskell"          ) :
    (rebindableSyntax,          "RebindableSyntax"         ) :
    (magicHash,                 "MagicHash"                ) :
    (overloadedStrings,         "OverloadedStrings"        ) :
    (noMonomorphismRestriction, "NoMonomorphismRestriction") :
    (scopedTypeVariables,       "ScopedTypeVariables"      ) :
    (cPP,                       "CPP"                      ) :
    (unicodeSyntax,             "UnicodeSyntax"            ) :
    (negativeLiterals,          "NegativeLiterals"         ) :
    (binaryLiterals,            "BinaryLiterals"           ) :
    (numDecimals,               "NumDecimals"              ) : []
  (!) = (!!)
  bangPatterns = [True] ! 0 where foo !bar = False
  templateHaskell = thc $(return (TupE []) :: ExpQ)
  rebindableSyntax = null (do { [()]; [()] })
    where _ >> _ = [] :: [()]
  magicHash = foo# () where
    foo = ['.']; "." # _  = False; foo# _ = True
  overloadedStrings = "" /= ""
  noMonomorphismRestriction = show foo == "0" where
    foo = 0
    bar = foo :: Double
  unicodeSyntax = let (★) = True in (*) where
    (*) = False
  negativeLiterals = -1 == NNa
  binaryLiterals = let b1 = 1 in 0b1 == 1
  numDecimals = show 0e0 == "0"
  scopedTypeVariables = stv (0 :: Double) == "0.0"
data{- = -} NN = NNa | NNb deriving Eq; instance Num NN where { fromInteger _ = NNa; negate _ = NNb; _ + _ = NNa; _ * _ = NNa; abs _ = NNa; signum _ = NNa }
instance{- = -} (Num a) => Num (e -> a) where { fromInteger = const . fromInteger; negate = (.) negate; abs = (.) abs; signum = (.) signum; x + y = e -> x e + y e; x * y = e -> x e * y e }
class THC a where { thc :: a -> Bool }; instance THC () where { thc _ = True }; instance THC (Q a) where { thc _ = False }; class (Show a, Num a) => STV a where
  stv :: a -> String
  stv = const $ show (f 0) where
    f = id :: a -> a
instance STV Double -- : 
end{code}

 # 
]>++++++++[<+++++++++>-]<+.>>++++[<++++++++++>-]<-.[-]>++++++++++ 
[<+++++++++++>-]<-.>>++++[<++++++++>-]<.>>++++++++++[<++++++++++> 
-]<- - -.<.>+.->>++++++++++[<+++++++++++>-]<++++.<.>>>++++++++++[ 
<++++++++++>-]<+++++.<<<<+.->>>>- - -.<+++.- - -<++.- ->>>>>+++++ 
+++++[<+++++++++++>-]<- - -.<<<<<.<+++.>>>.<<<-.- ->>>>+.<.<.<<.> 
++++++++++++++.[-]++++++++++""" else 0
 # 
from platform import * # 
print("I'm a Python program (%s %s)." % # [-][ 
(python_implementation(), python_version())); """--><html><head>
<!--:--><title>I'm a HTML page</title></head><body>
<!--:--><h1>I'm a <marquee><blink>horrible HTML</blink></marquee> page</h1>
<!--:--><script language="JavaScript">
<!--: # 
setTimeout( // 
   function () { // 
      document.body.innerHTML = "<h1>I'm a javascript-generated HTML page</h1>"; // 
   }, 10000); // 
//-->
</script><!--: 
</body></html><!-- }} # 
say "I'm a Perl6 program."; # """ # */
 #define FOO ]-->~

Квайн-полиглот

Для того чтобы написать квайн-полиглот на языках C# и Java, необходимо совместить принципы разработки квайна и полиглота. Как оказалось, и такая тема не нова: на сайте codegolf пользователи соревнуются, у кого квайн-полиглот или поликвайн получится короче: Write a Polyquine. Однако в этом вопросе не было вариантов с более многословными языками C# и Java, что мне и захотелось исправить.

Для написания такого квайна-полиглота нужно экранировать символы, запрещенные в строках обоих языков и встречающиеся в строках самой программы. Такими символами являются кавычки «, перенос строки n и обратный слеш . А чтобы еще уменьшить размер кода, повторяющиеся последовательности символов, такие как u000A, u002F и u002A, также были заменены на одиночные символы в самой кодирующей строке. Вот пример получившегося квайна-полиглота, валидного для компиляторов C# и Java:

PolyglotQuine.cs.java, 757 символов:

//u000Au002Fu002A
using System;//u002Au002F
class Program{public static void//u000Au002Fu002A
Main//u002Au002Fmain
(String[]z){String s="//@#'^using System;//'#^class Program{public static void//@#'^Main//'#main^(String[]z){String s=!$!,t=s;int[]a=new int[]{33,94,38,64,35,39,36};String[]b=new String[]{!&!!,!&n!,!&&!,!&@!,!&#!,!&'!,s};for(int i=0;i<7;i++)t=t.//@#'^Replace//'#replace^(!!+(char)a[i],b[i]);//@#'^Console.Write//'#System.out.printf^(t);}}",t=s;int[]a=new int[]{33,94,38,64,35,39,36};String[]b=new String[]{""","n","\","\u000A","\u002F","\u002A",s};for(int i=0;i<7;i++)t=t.//u000Au002Fu002A
Replace//u002Au002Freplace
(""+(char)a[i],b[i]);//u000Au002Fu002A
Console.Write//u002Au002FSystem.out.printf
(t);}}

Квайн-полиглот-палиндром

Еще больше усложним задачу: попробуем написать квайн-полиглот-палиндром. Напомню, что палиндром — это число или текст, одинаково читающиеся в обоих направлениях. Принцип разработки квайнов-палиндромов я описывал в статье 3 года назад, тоже в день программиста. Принцип кода-палиндрома заключается в том, что зеркальная часть программы помещается в однострочный или многострочный комментарий, что не мешает при компиляции. Простейший код-палиндром в C# выглядит следующим образом:

/**/class P{static void Main(){}};/*/;}}{)(niaM diov citats{P ssalc/**/

Как видим, многострочный комментарий /* начинается с середины и продолжается до конца, где и закрывается последовательностью */. Пустой комментарий в начале нужен для того, чтобы строка полностью стала симметричной.

Итак, объединяя принципы создания палиндрома, полиглота и квайна, я написал следующий код:

PalidromePolyglotQuine.cs.java, 1747 символов:

/**///u000Au002Fu002A
using System;//u002Au002F
class Program{public static void//u000Au002Fu002A
Main//u002Au002Fmain
(String[]z){String s="`**?`@#_^using System;?_#^class Program{public static void?@#_^Main?_#main^(String[]z){String s=!$!,t=s;int i;int[]a=new int[]{33,94,38,64,35,95,96,63,36};String[]b=new String[]{!&!!,!&n!,!&&!,!&@!,!&#!,!&_!,!`!,!?!,s};for(i=0;i<9;i++)t=t.?@#_^Replace?_#replace^(!!+(char)a[i],b[i]);t+='*';for(i=872;i>=0;i--)t=t+t?@#_^[i];Console.Write?_#.charAt(i);System.out.printf^(t);}}/",t=s;int i;int[]a=new int[]{33,94,38,64,35,95,96,63,36};String[]b=new String[]{""","n","\","\u000A","\u002F","\u002A","/","//",s};for(i=0;i<9;i++)t=t.//u000Au002Fu002A
Replace//u002Au002Freplace
(""+(char)a[i],b[i]);t+='*';for(i=872;i>=0;i--)t=t+t//u000Au002Fu002A
[i];Console.Write//u002Au002F.charAt(i);System.out.printf
(t);}}/*/}};)t(
ftnirp.tuo.metsyS;)i(tArahc.F200uA200u//etirW.elosnoC;]i[
A200uF200uA000u//t+t=t)--i;0=>i;278=i(rof;'*'=+t;)]i[b,]i[a)rahc(+""(
ecalperF200uA200u//ecalpeR
A200uF200uA000u//.t=t)++i;9<i;0=i(rof;}s,"//","/","A200u\","F200u\","A000u\","\","n","""{][gnirtS wen=b][gnirtS;}63,36,69,59,53,46,83,49,33{][tni wen=a][tni;i tni;s=t,"/}};)t(^ftnirp.tuo.metsyS;)i(tArahc.#_?etirW.elosnoC;]i[^_#@?t+t=t)--i;0=>i;278=i(rof;'*'=+t;)]i[b,]i[a)rahc(+!!(^ecalper#_?ecalpeR^_#@?.t=t)++i;9<i;0=i(rof;}s,!?!,!`!,!_&!,!#&!,!@&!,!&&!,!n&!,!!&!{][gnirtS wen=b][gnirtS;}63,36,69,59,53,46,83,49,33{][tni wen=a][tni;i tni;s=t,!$!=s gnirtS{)z][gnirtS(^niam#_?niaM^_#@?diov citats cilbup{margorP ssalc^#_?;metsyS gnisu^_#@`?**`"=s gnirtS{)z][gnirtS(
niamF200uA200u//niaM
A200uF200uA000u//diov citats cilbup{margorP ssalc
F200uA200u//;metsyS gnisu
A200uF200uA000u///**/

К сожалению, монстр получился слишком большим (1747 символов), однако это объясняется длинными цепочками юникод-символов и многословностью языков C# и Java. Уверен, что на других языках можно будет написать квайн-палиндром-полиглот гораздо меньшего размера.

Теперь проверим, как эта программа исполняется. Для начала избавимся от всех комментариев и правильно отформатируем код:

Отформатированный и очищенный код PalidromePolyglotQuine.cs.java под C#

using System;
class Program
{
    public static void Main(String[] z)
    {
        String s = "`**?`@#_^using System;?_#^class Program{public static void?@#_^Main?_#main^(String[]z){String s=!$!,t=s;int i;int[]a=new int[]{33,94,38,64,35,95,96,63,36};String[]b=new String[]{!&!!,!&n!,!&&!,!&@!,!&#!,!&_!,!`!,!?!,s};for(i=0;i<9;i++)t=t.?@#_^Replace?_#replace^(!!+(char)a[i],b[i]);t+='*';for(i=872;i>=0;i--)t=t+t?@#_^[i];Console.Write?_#.charAt(i);System.out.printf^(t);}}/",
            t = s;
        int i;
        int[] a = new int[] { 33, 94, 38, 64, 35, 95, 96, 63, 36 };
        String[] b = new String[] { """, "n", "\", "\u000A", "\u002F", "\u002A", "/", "//", s };
        for (i = 0; i < 9; i++)
            t = t.Replace("" + (char)a[i], b[i]);
        t += '*';
        for (i = 872; i >= 0; i--)
            t = t + t[i];
        Console.Write(t);
    }
}

Из листинга становится понятно, что переменная s содержит код программы, в котором экранированы запрещенные в строках символы и сжаты повторяющиеся последовательности. Ниже представлена таблица символов в формате код — символ — замена, которая используется для формирования выводимой строки t.

код символ замена
33 ! «
94 ^ n
38 &
64 @ u000A
35 # u002F
95 _ u002A
96 ` /
63 ? //
36 $ s

На следующем этапе происходит конкатенация строки t с ней же, инвертированной для того, чтобы на выходе получился палиндром. Стоит отметить, что число 872 (длина строки t) необходимо просчитывать после того, как квайн уже был написан. А для этого необходимо запускать уже написанный код, что также представляет определенные сложности.

Главным критерием при написании такого кода было достижение наименьшего размера в текстовом виде, а не его чистота. Поэтому, понятное дело, выглядит он несколько странно.

Как видим, ничего сложного в написании таких квайнов нет. Для тех, кто не верит, что это все действительно работает, были написаны тесты в репозитории Freaky-Sources (для запуска требуется, чтобы Java была установлена).

Кидайте в комментарии ваши квайнды-палиндромы-полиглоты на других языках и другие интересные кодофричные вещи, с удовольствием их изучу.

Еще раз поздравляю всех программистов и сочувствующих!

Improve Article

Save Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Given a string, the task is to check if the string can be made palindrome by swapping a character only once. 

    [NOTE: only one swap and only one character should be swapped with another character]

    Examples:  

    Input : bbg
    Output : true
    Explanation: Swap b(1st index) with g.
    
    Input : bdababd
    Output : true
    Explanation: Swap b(0th index) with d(last index) or
                 Swap d(1st index) with b(second index)
    
    Input : gcagac
    Output : false

    Approach:  

    This algorithm was based on a thorough analysis of the behavior and possibility of the forming string palindrome. By this analysis, I got the following conclusions :

    1. Firstly, we will be finding the differences in the string that actually prevents it from being a palindrome. 
      1. To do this, We will start from both the ends and comparing one element from each end at a time, whenever it does match we store the values in a separate array as along with this we keep a count on the number of unmatched items.   
    2. If the number of unmatched items is more than 2, it is never possible to make it a palindrome string by swapping only one character.   
    3. If (number of unmatched items = 2) – it is possible to make the string palindrome if the characters present in first unmatched set are same as the characters present in second unmatched set. (For example : try this out “bdababd”).
    4.  If (number of unmatched items = 1) 
      1. if (length of string is even) – it is not possible to make a palindrome string out of this. 
      2. if (length of string is odd) – it is possible to make a palindrome string out of this if one of the unmatched character matches with the middle character.
    5. If (number of unmatched items = 0) – palindrome is possible if we swap the position of any same characters.

    Implementation:

    C++

    #include <bits/stdc++.h>

    using namespace std;

    bool isPalindromePossible(string input)

    {

        int len = input.length();

        int diffCount = 0, i;

        char diff[2][2];

        for (i = 0; i < len / 2; i++)

        {

            if (input[i] != input[len - i - 1])

            {

                if (diffCount == 2) return false;

                diff[diffCount][0] = input[i];

                diff[diffCount++][1] = input[len - i - 1];

            }

        }

        switch (diffCount)

        {

            case 0:

                return true;

            case 1:

            {

                char midChar = input[i];

                if (len % 2 != 0 and

                   (diff[0][0] == midChar or

                    diff[0][1] == midChar))

                    return true;

            }

            case 2:

                if ((diff[0][0] == diff[1][0] and

                     diff[0][1] == diff[1][1]) or

                    (diff[0][0] == diff[1][1] and

                     diff[0][1] == diff[1][0]))

                    return true;

        }

        return false;

    }

    int main()

    {

        cout << boolalpha

             << isPalindromePossible("bbg") << endl;

        cout << boolalpha

             << isPalindromePossible("bdababd") << endl;

        cout << boolalpha

             << isPalindromePossible("gcagac") << endl;

        return 0;

    }

    Java

    class GFG

     {

        public static boolean isPalindromePossible(String input)

        {

            char[] charStr = input.toCharArray();

            int len = input.length(), i;

            int diffCount = 0;

            char[][] diff = new char[2][2];

            for (i = 0; i < len / 2; i++) {

                if (charStr[i] != charStr[len - i - 1]) {

                    if (diffCount == 2)

                        return false;

                    diff[diffCount][0] = charStr[i];

                    diff[diffCount++][1] = charStr[len - i - 1];

                }

            }

            switch (diffCount) {

            case 0:

                return true;

            case 1:

                char midChar = charStr[i];

                if (len % 2 != 0 && (diff[0][0] == midChar

                                     || diff[0][1] == midChar))

                    return true;

            case 2:

                if ((diff[0][0] == diff[1][0] && diff[0][1] == diff[1][1])

                    || (diff[0][0] == diff[1][1] && diff[0][1] == diff[1][0]))

                    return true;

            }

            return false;

        }

        public static void main(String[] args)

        {

            System.out.println(isPalindromePossible("bbg"));

            System.out.println(isPalindromePossible("bdababd"));

            System.out.println(isPalindromePossible("gcagac"));

        }

    }

    Python3

    def isPalindromePossible(input: str) -> bool:

        length = len(input)

        diffCount = 0

        i = 0

        diff = [['0'] * 2] * 2

        while i < length // 2:

            if input[i] != input[length - i - 1]:

                if diffCount == 2:

                    return False

                diff[diffCount][0] = input[i]

                diff[diffCount][1] = input[length - i - 1]

                diffCount += 1

            i += 1

        if diffCount == 0:

            return True

        elif diffCount == 1:

            midChar = input[i]

            if length % 2 != 0 and (diff[0][0] == midChar

                                    or diff[0][1] == midChar):

                return True

        elif diffCount == 2:

            if (diff[0][0] == diff[1][0] and diff[0][1] == diff[1][1]) or (

                    diff[0][0] == diff[1][1] and diff[0][1] == diff[1][0]):

                return True

        return False

    if __name__ == "__main__":

        print(isPalindromePossible("bbg"))

        print(isPalindromePossible("bdababd"))

        print(isPalindromePossible("gcagac"))

    C#

    using System;

    class GFG

    {

        public static bool isPalindromePossible(String input)

        {

            char[] charStr = input.ToCharArray();

            int len = input.Length, i;

            int diffCount = 0;

            char[,] diff = new char[2, 2];

            for (i = 0; i < len / 2; i++)

            {

                if (charStr[i] != charStr[len - i - 1])

                {

                    if (diffCount == 2)

                        return false;

                    diff[diffCount, 0] = charStr[i];

                    diff[diffCount++, 1] = charStr[len - i - 1];

                }

            }

            switch (diffCount)

            {

                case 0:

                    return true;

                case 1:

                    char midChar = charStr[i];

                if (len % 2 != 0 &&

                    (diff[0,0] == midChar ||

                    diff[0,1] == midChar))

                    return true;

                break;

                case 2:

                if ((diff[0,0] == diff[1,0] &&

                     diff[0,1] == diff[1,1]) ||

                     (diff[0,0] == diff[1,1] &&

                     diff[0,1] == diff[1,0]))

                    return true;

                break;

            }

            return false;

        }

        public static void Main(String[] args)

        {

            Console.WriteLine(isPalindromePossible("bbg"));

            Console.WriteLine(isPalindromePossible("bdababd"));

            Console.WriteLine(isPalindromePossible("gcagac"));

        }

    }

    Javascript

    <script>

    function isPalindromePossible(input){

        let Length = input.length

        let diffCount = 0,i = 0

        let diff = new Array(2).fill(0).map(()=>new Array(2))

        for (i = 0; i < Math.floor(Length / 2); i++)

        {

            if(input[i] != input[Length - i - 1]){

                if(diffCount == 2)

                    return false

                diff[diffCount][0] = input[i]

                diff[diffCount++][1] = input[Length - i - 1]

            }

        }

        switch (diffCount)

        {

            case 0:

                return true

            case 1:

            {

                let midChar = input[i]

                if (Length % 2 != 0 &&

                   (diff[0][0] == midChar ||

                    diff[0][1] == midChar))

                    return true

            }

            case 2:

                if ((diff[0][0] == diff[1][0] &&

                     diff[0][1] == diff[1][1]) ||

                    (diff[0][0] == diff[1][1] &&

                     diff[0][1] == diff[1][0]))

                    return true

        }

        return false

    }

    document.write(isPalindromePossible("bbg"),"</br>")

    document.write(isPalindromePossible("bdababd"),"</br>")

    document.write(isPalindromePossible("gcagac"),"</br>")

    </script>

    Complexity Analysis:

    • Time Complexity : O(n) 
    • Auxiliary Space : O(1) 

    Improve Article

    Save Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Given a string, the task is to check if the string can be made palindrome by swapping a character only once. 

    [NOTE: only one swap and only one character should be swapped with another character]

    Examples:  

    Input : bbg
    Output : true
    Explanation: Swap b(1st index) with g.
    
    Input : bdababd
    Output : true
    Explanation: Swap b(0th index) with d(last index) or
                 Swap d(1st index) with b(second index)
    
    Input : gcagac
    Output : false

    Approach:  

    This algorithm was based on a thorough analysis of the behavior and possibility of the forming string palindrome. By this analysis, I got the following conclusions :

    1. Firstly, we will be finding the differences in the string that actually prevents it from being a palindrome. 
      1. To do this, We will start from both the ends and comparing one element from each end at a time, whenever it does match we store the values in a separate array as along with this we keep a count on the number of unmatched items.   
    2. If the number of unmatched items is more than 2, it is never possible to make it a palindrome string by swapping only one character.   
    3. If (number of unmatched items = 2) – it is possible to make the string palindrome if the characters present in first unmatched set are same as the characters present in second unmatched set. (For example : try this out “bdababd”).
    4.  If (number of unmatched items = 1) 
      1. if (length of string is even) – it is not possible to make a palindrome string out of this. 
      2. if (length of string is odd) – it is possible to make a palindrome string out of this if one of the unmatched character matches with the middle character.
    5. If (number of unmatched items = 0) – palindrome is possible if we swap the position of any same characters.

    Implementation:

    C++

    #include <bits/stdc++.h>

    using namespace std;

    bool isPalindromePossible(string input)

    {

        int len = input.length();

        int diffCount = 0, i;

        char diff[2][2];

        for (i = 0; i < len / 2; i++)

        {

            if (input[i] != input[len - i - 1])

            {

                if (diffCount == 2) return false;

                diff[diffCount][0] = input[i];

                diff[diffCount++][1] = input[len - i - 1];

            }

        }

        switch (diffCount)

        {

            case 0:

                return true;

            case 1:

            {

                char midChar = input[i];

                if (len % 2 != 0 and

                   (diff[0][0] == midChar or

                    diff[0][1] == midChar))

                    return true;

            }

            case 2:

                if ((diff[0][0] == diff[1][0] and

                     diff[0][1] == diff[1][1]) or

                    (diff[0][0] == diff[1][1] and

                     diff[0][1] == diff[1][0]))

                    return true;

        }

        return false;

    }

    int main()

    {

        cout << boolalpha

             << isPalindromePossible("bbg") << endl;

        cout << boolalpha

             << isPalindromePossible("bdababd") << endl;

        cout << boolalpha

             << isPalindromePossible("gcagac") << endl;

        return 0;

    }

    Java

    class GFG

     {

        public static boolean isPalindromePossible(String input)

        {

            char[] charStr = input.toCharArray();

            int len = input.length(), i;

            int diffCount = 0;

            char[][] diff = new char[2][2];

            for (i = 0; i < len / 2; i++) {

                if (charStr[i] != charStr[len - i - 1]) {

                    if (diffCount == 2)

                        return false;

                    diff[diffCount][0] = charStr[i];

                    diff[diffCount++][1] = charStr[len - i - 1];

                }

            }

            switch (diffCount) {

            case 0:

                return true;

            case 1:

                char midChar = charStr[i];

                if (len % 2 != 0 && (diff[0][0] == midChar

                                     || diff[0][1] == midChar))

                    return true;

            case 2:

                if ((diff[0][0] == diff[1][0] && diff[0][1] == diff[1][1])

                    || (diff[0][0] == diff[1][1] && diff[0][1] == diff[1][0]))

                    return true;

            }

            return false;

        }

        public static void main(String[] args)

        {

            System.out.println(isPalindromePossible("bbg"));

            System.out.println(isPalindromePossible("bdababd"));

            System.out.println(isPalindromePossible("gcagac"));

        }

    }

    Python3

    def isPalindromePossible(input: str) -> bool:

        length = len(input)

        diffCount = 0

        i = 0

        diff = [['0'] * 2] * 2

        while i < length // 2:

            if input[i] != input[length - i - 1]:

                if diffCount == 2:

                    return False

                diff[diffCount][0] = input[i]

                diff[diffCount][1] = input[length - i - 1]

                diffCount += 1

            i += 1

        if diffCount == 0:

            return True

        elif diffCount == 1:

            midChar = input[i]

            if length % 2 != 0 and (diff[0][0] == midChar

                                    or diff[0][1] == midChar):

                return True

        elif diffCount == 2:

            if (diff[0][0] == diff[1][0] and diff[0][1] == diff[1][1]) or (

                    diff[0][0] == diff[1][1] and diff[0][1] == diff[1][0]):

                return True

        return False

    if __name__ == "__main__":

        print(isPalindromePossible("bbg"))

        print(isPalindromePossible("bdababd"))

        print(isPalindromePossible("gcagac"))

    C#

    using System;

    class GFG

    {

        public static bool isPalindromePossible(String input)

        {

            char[] charStr = input.ToCharArray();

            int len = input.Length, i;

            int diffCount = 0;

            char[,] diff = new char[2, 2];

            for (i = 0; i < len / 2; i++)

            {

                if (charStr[i] != charStr[len - i - 1])

                {

                    if (diffCount == 2)

                        return false;

                    diff[diffCount, 0] = charStr[i];

                    diff[diffCount++, 1] = charStr[len - i - 1];

                }

            }

            switch (diffCount)

            {

                case 0:

                    return true;

                case 1:

                    char midChar = charStr[i];

                if (len % 2 != 0 &&

                    (diff[0,0] == midChar ||

                    diff[0,1] == midChar))

                    return true;

                break;

                case 2:

                if ((diff[0,0] == diff[1,0] &&

                     diff[0,1] == diff[1,1]) ||

                     (diff[0,0] == diff[1,1] &&

                     diff[0,1] == diff[1,0]))

                    return true;

                break;

            }

            return false;

        }

        public static void Main(String[] args)

        {

            Console.WriteLine(isPalindromePossible("bbg"));

            Console.WriteLine(isPalindromePossible("bdababd"));

            Console.WriteLine(isPalindromePossible("gcagac"));

        }

    }

    Javascript

    <script>

    function isPalindromePossible(input){

        let Length = input.length

        let diffCount = 0,i = 0

        let diff = new Array(2).fill(0).map(()=>new Array(2))

        for (i = 0; i < Math.floor(Length / 2); i++)

        {

            if(input[i] != input[Length - i - 1]){

                if(diffCount == 2)

                    return false

                diff[diffCount][0] = input[i]

                diff[diffCount++][1] = input[Length - i - 1]

            }

        }

        switch (diffCount)

        {

            case 0:

                return true

            case 1:

            {

                let midChar = input[i]

                if (Length % 2 != 0 &&

                   (diff[0][0] == midChar ||

                    diff[0][1] == midChar))

                    return true

            }

            case 2:

                if ((diff[0][0] == diff[1][0] &&

                     diff[0][1] == diff[1][1]) ||

                    (diff[0][0] == diff[1][1] &&

                     diff[0][1] == diff[1][0]))

                    return true

        }

        return false

    }

    document.write(isPalindromePossible("bbg"),"</br>")

    document.write(isPalindromePossible("bdababd"),"</br>")

    document.write(isPalindromePossible("gcagac"),"</br>")

    </script>

    Complexity Analysis:

    • Time Complexity : O(n) 
    • Auxiliary Space : O(1) 

    Понравилась статья? Поделить с друзьями:
  • Как написать пак чимин на английском
  • Как написать павлу иванову
  • Как написать павлу воле письмо
  • Как написать ощущаю
  • Как написать ошиблась правильно