Уотсон-Крик палиндромы


31

проблема

Создайте функцию, которая может определить, является ли произвольная строка ДНК палиндромом Уотсона-Крика. Функция возьмет строку ДНК и выведет истинное значение, если строка является палиндромом Уотсона-Крика, и ложное значение, если это не так. (Истина и Ложь также могут быть представлены как 1 и 0, соответственно.)

Строка ДНК может быть в верхнем или нижнем регистре в зависимости от ваших предпочтений.

Кроме того, строка ДНК не будет пустой.

объяснение

Строка ДНК - это палиндром Уотсона-Крика, когда комплемент его реверса равен самому себе.

Если дана строка ДНК, сначала поменяйте ее местами, а затем дополните каждый символ согласно основаниям ДНК (A A T и C ↔ G). Если исходная строка равна дополненной обратной строке, это палиндром Уотсона-Крика.

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

Цель

Это код-гольф, и выигрывает самый короткий код.

Тестовые случаи

Формат есть <input> = <output>.

ATCGCGAT = true
AGT = false
GTGACGTCAC = true
GCAGTGA = false
GCGC = true
AACTGCGTTTAC = false
ACTG = false


3
Кто-то должен написать программу на ДНК, которая также является палиндромом Уотсона-Крика. : D (возможно, это не возможно)
mbomb007

Или, если хотите, «слово является палиндромом Уотсона-Крика, если оно имеет порядок 2 в свободной группе на 2 генераторах» (или на n генераторах!).
wchargin

(Технически, я думаю, это «максимум 2 порядка»)
wchargin

1
@AndrasDeak Согласно книге Уотсона, Франклин был, по-видимому, главным образом шипом на их стороне. Она неоднократно отказывалась сдавать рентгеновские снимки, показывающие спираль (насколько я помню), потому что она отказывалась в это верить. Это стоит прочитать, если вы заинтересованы в открытии в любом случае.
Обсидиановый Феникс

Ответы:


27

05AB1E , 10 7 байтов

Код:

Â'š×‡Q

Объяснение:

Чтобы проверить, является ли строка палиндромом, нам просто нужно проверить ввод с помощью ввода, с at поменять местами и cgпоменять местами, а затем развернуть его. Вот что мы собираемся сделать. Мы нажимаем на вход и ввод с обратной стороны Â(раздвоение). Теперь начинается сложная часть. 'š×это сжатая версия для creating. Если мы изменим это, вы поймете, почему это в коде:

CreATinG
|  ||  |
GniTAerC

Это будет использоваться для транслитерации обратного ввода. Транслитерация делается с . После этого мы просто проверяем правильность ввода и транслитерированного ввода Qи печатаем это значение. Вот как выглядит стек для ввода actg:

          # ["actg", "gtca"]
 'š×       # ["actg", "gtca", "creating"]
    Â      # ["actg", "gtca", "creating", "gnitaerc"]
     ‡     # ["actg", "cagt"]
      Q    # [0]

Что также можно увидеть с флагом отладки ( попробуйте здесь ).

Использует кодировку CP-1252 . Попробуйте онлайн!,


4
Очень, э, креативно ...
Тоби Спейт

2
У этого языка есть некоторые очень опрятные особенности
мили

18

Желе , 9 байт

O%8µ+U5ḍP

Попробуйте онлайн!или проверьте все контрольные примеры .

Как это работает

O%8µ+U5ḍP  Main link. Argument: S (string)

O          Compute the code points of all characters.
 %8        Compute the residues of division by 8.
           This maps 'ACGT' to [1, 3, 7, 4].
   µ       Begin a new, monadic link. Argument: A (array of residues)
    +U     Add A and A reversed.
      5ḍ   Test the sums for divisibility by 5.
           Of the sums of all pairs of integers in [1, 3, 7, 4], only 1 + 4 = 5
           and 3 + 7 = 10 are divisible by 5, thus identifying the proper pairings.
        P  Take the product of the resulting Booleans.

4
Я думаю, что Python довольно близок к тому, чтобы конкурировать с этим ответом! Сравните первые девять байт моего ответа: lambda s:. Это почти полное решение!
orlp

Подождите, часть "Как это работает" не совсем объясняет, как это работает ... Почему остатки 8 и суммы 5 ?? Где буквы дополняются?
ZeroOne

@ ZeroOne Я уточнил эту часть.
Деннис,

Ух ты! Это чертовски умно. :) Благодарность!
ZeroOne


8

Perl, 27 байт

Включает +2 для -lp

Введите данные на STDIN, печатает 1 или ничего:

dnapalin.pl <<< ATCGCGAT

dnapalin.pl:

#!/usr/bin/perl -lp
$_=y/ATCG/TAGC/r=~reverse

Заменить $_=на, $_+=чтобы получить 0вместо пустого для ложного случая



7

Retina , 34 33 байта

$
;$_
T`ACGT`Ro`;.+
+`(.);\1
;
^;

Попробуйте онлайн!(Немного изменен для запуска всех тестовых случаев одновременно.)

объяснение

$
;$_

Продублируйте ввод, сопоставив конец строки и вставив ;после него весь ввод.

T`ACGT`Ro`;.+

Сопоставьте только вторую половину входных данных ;.+и выполните замену пар транслитерацией. Что касается целевого набора Ro: oссылается на другой набор, то oесть заменяется на ACGT. Но Rпереворачивает этот набор, поэтому два набора на самом деле:

ACGT
TGCA

Если вход является палиндромом ДНК, теперь у нас будет вход, за которым следует его обратное (разделенное ;).

+`(.);\1
;

Повторно ( +) удалить пару одинаковых символов вокруг ;. Это будет продолжаться до тех пор, пока ;не останется только символ или, пока два символа вокруг ;не перестанут совпадать, что будет означать, что строки не противоположны друг другу.

^;

Проверьте, есть ли первый символ ;и напечатайте 0или 1соответственно.


6

JavaScript (ES6), 59 байт

f=s=>!s||/^(A.*T|C.*G|G.*C|T.*A)$/.test(s)&f(s.slice(1,-1))

Лучшее, что я мог сделать без использования Regexp - 62 байта:

f=s=>!s||parseInt(s[0]+s.slice(-1),33)%32%7<1&f(s.slice(1,-1))

5

Руби, 35

Я пробовал другие способы, но очевидный путь был самым коротким:

->s{s.tr('ACGT','TGCA').reverse==s}

в тестовой программе

f=->s{s.tr('ACGT','TGCA').reverse==s}

puts f['ATCGCGAT']
puts f['AGT']
puts f['GTGACGTCAC']
puts f['GCAGTGA']
puts f['GCGC']
puts f['AACTGCGTTTAC'] 

2
->s{s.==s.reverse.tr'ACGT','TGCA'}на байт короче
Митч Шварц

@ MitchSchwartz Ничего себе, это работает, но я понятия не имею, для чего это сначала .. Код выглядит для меня более правильным без него, но он необходим для его запуска. Это где-нибудь задокументировано?
Уровень Река Сент

Вы уверены, что не хотите понять это самостоятельно?
Митч Шварц

@MitchSchwartz хахаха я уже пробовал. Я нахожу требования ruby ​​к пробелам очень своеобразными. Странные требования к периодам - ​​это совсем другой вопрос. У меня есть несколько теорий, но все они могут ошибаться. Я подозреваю, что это может быть связано с трактовкой ==как метода, а не как оператора, но поиск по символам невозможен.
Уровень Река St

Вы подозревали правильно. :) Это просто старый вызов метода.
Митч Шварц

5

Haskell, 48 45 байт

(==)=<<reverse.map((cycle"TCG_A"!!).fromEnum)

Пример использования: (==)=<<reverse.map((cycle"_T_GA__C"!!).fromEnum) $ "ATCGCGAT"-> True.

Неточечная версия

f x = reverse (map h x) == x           -- map h to x, reverse and compare to x
h c = cycle "TCG_A" !! fromEnum c      -- take the ascii-value of c and take the
                                       -- char at this position of string
                                       -- "TCG_ATCG_ATCG_ATCG_A..."

Изменить: @Mathias Dolidon сохранил 3 байта. Благодарность!


Работает с cycle "TCG_A" тоже. :)
Матиас Долидон


4

Юлия, 47 38 байт

s->((x=map(Int,s)%8)+reverse(x))%50

Это анонимная функция, которая принимает Char массив и возвращает логическое значение. Чтобы вызвать его, назначьте его переменной.

При этом используется алгоритм Денниса, который короче наивного решения. Мы получаем остаток каждой кодовой точки, деленный на 8, добавляем его к себе в обратном порядке, получаем остатки от деления на 5 и проверяем, все ли равны 0. Последний шаг выполняется с помощью инфиксной версии issubset, которая приводит оба аргумента к Setперед проверкой. Это означает , что [0,0,0]определяется как подмножество 0, так как Set([0,0,0]) == Set(0). Это короче, чем явная проверка на 0.

Попробуйте онлайн!

Сохранено 9 байтов благодаря Денису!





3

Oracle SQL 11.2, 68 байт

SELECT DECODE(TRANSLATE(REVERSE(:1),'ATCG','TAGC'),:1,1,0)FROM DUAL; 

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

3

Юлия 0,4, 22 байта

s->s$reverse(s)⊆""

Строка содержит управляющие символы EOT (4) и NAK (21). Ввод должен быть в форме массива символов.

Этот подход выполняет XOR для символов ввода с соответствующими символами в обратном вводе. Для правильных пар это приводит к символам EOT или NAK. Проверка на включение в строку этих символов выдает желаемое логическое значение.

Попробуйте онлайн!


3

С, 71

r,e;f(char*s){for(r=0,e=strlen(s)+1;*s;s++)r|=*s*s[e-=2]%5^2;return!r;}

2 байта сохранены Денисом. Дополнительные 2 байта сохранены путем адаптации для ввода строчных букв: константы 37и 21пересмотрены до 5и2 .

С 75

i,j;f(char*s){for(i=j=0;s[i];i++)j|=s[i]*s[strlen(s)-i-1]%37!=21;return!j;}

Сохранение одного байта: исключены скобки, взяв произведение двух кодов ASCII, мода 37. Допустимые пары оцениваются как 21. Предполагается ввод заглавными буквами.

С, 76

i,j;f(char*s){for(i=j=0;s[i];i++)j|=(s[i]+s[strlen(s)-i-1])%11!=6;return!j;}

Использует тот факт, что коды ASCII действительных пар составляют 138 или 149. Когда используется mod 11, это единственные пары, сумма которых равна 6. Предполагается ввод в верхнем регистре.

разряженный в тестовой программе

i,j;

f(char *s){
   for(i=j=0;s[i];i++)                  //initialize i and j to 0; iterate i through the string
     j|=(s[i]+s[strlen(s)-i-1])%11!=6;  //add characters at i from each end of string, take result mod 11. If not 6, set j to 1
return!j;}                              //return not j (true if mismatch NOT detected.)

main(){
  printf("%d\n", f("ATCGCGAT"));
  printf("%d\n", f("AGT"));
  printf("%d\n", f("GTGACGTCAC"));
  printf("%d\n", f("GCAGTGA"));
  printf("%d\n", f("GCGC"));
  printf("%d\n", f("AACTGCGTTTAC"));
} 

1
r,e;f(char*s){for(r=0,e=strlen(s)+1;*s;s++)r|=*s*s[e-=2]%37^21;return!r;}сохраняет пару байтов.
Деннис

@ Денис, спасибо, я действительно не был в настроении для изменения указателей, но он выдавил байт! Я должен был увидеть !=> ^себя. Я уменьшил еще 2, переключившись на строчные буквы: оба магических числа теперь однозначные.
Уровень Река St

3

фактор , 72 байта

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

[ dup reverse [ { { 67 71 } { 65 84 } { 71 67 } { 84 65 } } at ] map = ]

Обратный, ищите таблицу, сравнивайте равные.


Вау, это много пробелов !!! Это все необходимо? Также была бы полезна ссылка на домашнюю страницу языка.
Уровень Река St

@LevelRiverSt К сожалению, все это необходимо. Я добавлю ссылку в шапку.
кот

3

Bash + coreutils, 43 32 байта

[ `tr ATCG TAGC<<<$1|rev` = $1 ]

тесты:

for i in ATCGCGAT AGT GTGACGTCAC GCAGTGA GCGC AACTGCGTTTAC; do ./78410.sh $i && echo $i = true || echo $i = false; done
ATCGCGAT = true
AGT = false
GTGACGTCAC = true
GCAGTGA = false
GCGC = true
AACTGCGTTTAC = false

3

J - 21 байт

0=[:+/5|[:(+|.)8|3&u:

По методу Денниса

использование

   f =: 0=[:+/5|[:(+|.)8|3&u:
   f 'ATCGCGAT'
1
   f 'AGT'
0
   f 'GTGACGTCAC'
1
   f 'GCAGTGA'
0
   f 'GCGC'
1
   f 'AACTGCGTTTAC'
0
   f 'ACTG'
0

объяснение

0=[:+/5|[:(+|.)8|3&u:
                 3&u:    - Convert from char to int
               8|        - Residues from division by 8 for each
            |.           - Reverse the list
           +             - Add from the list and its reverse element-wise
        [:               - Cap, compose function
      5|                 - Residues from division by 5 for each
    +/                   - Fold right using addition to create a sum
  [:                     - Cap, compose function
0=                       - Test the sum for equality to zero

3

Лабиринт , 42 байта

_8
,%
;
"}{{+_5
"=    %_!
 = """{
 ;"{" )!

Завершается ошибкой деления на ноль (сообщение об ошибке в STDERR).

Попробуйте онлайн!

Расположение кажется действительно неэффективным, но я просто не вижу способа играть в гольф прямо сейчас.

объяснение

Это решение основано на арифметическом приеме Денниса: возьмите все коды символов по модулю 8, добавьте пару с обоих концов и убедитесь, что она делится на 5.

Лабиринтный праймер:

  • Лабиринт имеет два набора целых чисел произвольной точности, main и aux (ily), которые изначально заполнены (неявным) бесконечным количеством нулей.
  • Исходный код напоминает лабиринт, где указатель инструкций (IP) следует за коридорами, когда это возможно (даже за углами). Код начинается с первого действительного символа в порядке чтения, то есть в этом случае в верхнем левом углу. Когда IP-адрес достигает какой-либо формы соединения (то есть нескольких соседних ячеек в дополнение к той, из которой он получен), он выбирает направление на основе вершины основного стека. Основные правила: поверните налево при отрицательном значении, продолжайте движение вперед при нулевом, поверните направо при положительном. И когда один из них невозможен из-за наличия стены, тогда IP будет идти в противоположном направлении. IP также оборачивается при попадании в тупик.
  • Цифры обрабатываются путем умножения вершины основного стека на 10 и последующего добавления цифры.

Код начинается с небольшого цикла 2x2 по часовой стрелке, который читает все входные данные по модулю 8:

_   Push a 0.
8   Turn into 8.
%   Modulo. The last three commands do nothing on the first iteration
    and will take the last character code modulo 8 on further iterations.
,   Read a character from STDIN or -1 at EOF. At EOF we will leave loop.

Сейчас ;сбрасывает -1. Мы вводим еще один цикл по часовой стрелке, который перемещает верхнюю часть основного стека (т.е. последний символ) вниз:

"   No-op, does nothing.
}   Move top of the stack over to aux. If it was at the bottom of the stack
    this will expose a zero underneath and we leave the loop.
=   Swap top of main with top of aux. The effect of the last two commands
    together is to move the second-to-top stack element from main to aux.
"   No-op.

Теперь есть короткий линейный бит:

{{  Pull two characters from aux to main, i.e. the first and last (remaining)
    characters of the input (mod 8).
+   Add them.
_5  Push 5.
%   Modulo.

Теперь IP находится на стыке, который действует как ветвь для проверки делимости на 5. Если результат по модулю не равен нулю, мы знаем, что входной сигнал не является палиндромом Уотсона-Крика, и мы поворачиваем на восток:

_   Push 0.
!   Print it. The IP hits a dead end and turns around.
_   Push 0.
%   Try to take modulo, but division by zero fails and the program terminates.

В противном случае мы должны продолжать проверять оставшуюся часть ввода, чтобы IP продолжал двигаться на юг. В {тянет за нижнюю часть оставшегося входа. Если мы исчерпали ввод, то это будет 0(снизу aux ), и IP продолжает двигаться на юг:

)   Increment 0 to 1.
!   Print it. The IP hits a dead end and turns around.
)   Increment 0 to 1.
{   Pull a zero over from aux, IP keeps moving north.
%   Try to take modulo, but division by zero fails and the program terminates.

В противном случае в строке будет проверено больше символов. IP поворачивается на запад и переходит в следующий (по часовой стрелке) цикл 2x2, который в основном состоит из no-ops:

"   No-op.
"   No-op.
{   Pull one value over from aux. If it's the bottom of aux, this will be
    zero and the IP will leave the loop eastward.
"   No-op.

После этого цикла мы снова получаем ввод в основной стек, за исключением его первого и последнего символа и с нулем сверху. В ;отбрасывает , 0а затем =меняет местами вершины стеков, но это просто отменить первое =в цикле, потому что мы вступаем в цикл в другом месте. Промыть и повторить.


3

sed, 67 61 байт

G;H;:1;s/\(.\)\(.*\n\)/\2\1/;t1;y/ACGT/TGCA/;G;s/^\(.*\)\1$/1/;t;c0

(67 байт)

Тест

for line in ATCGCGAT AGT GTGACGTCAC GCAGTGA GCGC AACTGCGTTTAC ACTG
do echo -n "$line "
    sed 'G;H;:1;s/\(.\)\(.*\n\)/\2\1/;t1;y/ACGT/TGCA/;G;s/^\(.*\)\1$/1/;t;c0' <<<"$line"
done

Выход

ATCGCGAT 1
AGT 0
GTGACGTCAC 1
GCAGTGA 0
GCGC 1
AACTGCGTTTAC 0
ACTG 0

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

sed -r 'G;H;:1;s/(.)(.*\n)/\2\1/;t1;y/ACGT/TGCA/;G;s/^(.*)\1$/1/;t;c0'

Если вы можете сделать это в 61 байт, то это ваш счет - нет ничего против NFA или регулярного выражения Тьюринга в этой конкретной задаче. Некоторые задачи полностью запрещают регулярное выражение, но обычно только регулярное гольф запрещает не регулярные выражения.
кот

3

C #, 65 байт

bool F(string s)=>s.SequenceEqual(s.Reverse().Select(x=>"GACT"[("GACT".IndexOf(x)+2)%4]));

В некоторых случаях в .NET есть довольно длинные имена методов фреймворка, что не обязательно делает лучший фреймворк для гольф-кода. В этом случае имена методов каркаса составляют 33 символа из 90. :)

На основе трюка модуля из другого места в потоке:

bool F(string s)=>s.Zip(s.Reverse(),(a,b)=>a%8+b%8).All(x=>x%5==0);

Теперь весит 67 символов, из которых 13 являются именами методов.

Еще одна небольшая оптимизация, чтобы сбить колоссальные 2 символа:

bool F(string s)=>s.Zip(s.Reverse(),(a,b)=>(a%8+b%8)%5).Sum()<1;

Итак, 65 из которых 13 являются каркасными именами.

Редактировать: исключение некоторых из ограниченных «шаблонов» из решения и добавление пары условий оставляет нас с выражением

s.Zip(s.Reverse(),(a,b)=>(a%8+b%8)%5).Sum()

Что дает 0 тогда и только тогда, когда строка s является правильным ответом. Как указывает cat, «bool F (string s) =>» на самом деле заменяется на «s =>», если в коде в противном случае ясно, что выражение является a Func<string,bool>, т.е. сопоставляет строку с логическим значением.


1
Добро пожаловать в PPCG, хороший первый ответ! : D
кошка

@cat Спасибо за это! :)
robhol

1
Я не очень знаю , C #, но если это лямбда, то вы можете оставить его тип и назначение его, так как анонимные функции в порядке, пока они правопреемник состояния .
кот

1
Кроме того, вы не можете сделать !s.Zip...вместо s.Zip...==0? (Или вы не можете !использовать его в C #?) Даже если вы не можете логически отрицать это, вы можете не указывать в своем ответе никаких инверсий и утверждать, что это возвращает <this thing> для false и <this другой детерминированный, явно заметная вещь> для правды.
кот

1
@cat: Вы правы насчет отбрасывания шрифта. Я думал, что код должен быть непосредственно исполняемым, но простые предположения о вводе и выводе делают его немного проще. Однако, другая вещь не сработает - на мой взгляд, это правильно, поскольку логическая операция не имеет логического (тонового) способа применить к числу. Присвоение 0 и 1 значений false и true, в конце концов, просто соглашение.
Робхол


2

R 101 байт

g=function(x){y=unlist(strsplit(x,""));all(sapply(rev(y),switch,"C"="G","G"="C","A"="T","T"="A")==y)}

Тестовые случаи

g("ATCGCGAT")
[1] TRUE
g("AGT")
[1] FALSE
g("GTGACGTCAC")
[1] TRUE
g("GCAGTGA")
[1] FALSE
g("GCGC")
[1] TRUE
g("AACTGCGTTTAC")
[1] FALSE
g("ACTG")
[1] FALSE

strsplit(x,"")[[1]]на 3 байта короче unlist(strsplit(x,""))и здесь эквивалентно, так xкак всегда представляет собой одну строку символов.
plannapus

2

Октава, 52 байта

f=@(s) prod(mod((i=mod(toascii(s),8))+flip(i),5)==0)

Следуя трюку Дениса ... возьмите значение 8 ASCII, переверните и сложите вместе; если каждая сумма кратна пяти, вы золотые.


Это один пробел значителен? Это странно.
кошка

Кроме того, вы можете пропустить это f=задание; безымянные функции в порядке.
кот

1

Clojure / ClojureScript, 49 символов

#(=(list* %)(map(zipmap"ATCG""TAGC")(reverse %)))

Работает на струнах. Если требования ослаблены, чтобы разрешить списки, я могу снять (list* )и сохранить 7 символов.


1

R 70 байт

f=function(x)all(chartr("GCTA","CGAT",y<-strsplit(x,"")[[1]])==rev(y))

Использование:

> f=function(x)all(chartr("GCTA","CGAT",y<-strsplit(x,"")[[1]])==rev(y))
> f("GTGACGTCAC")
[1] TRUE
> f("AACTGCGTTTAC")
[1] FALSE
> f("AGT")
[1] FALSE
> f("ATCGCGAT")
[1] TRUE

1

C 71 байт

Требуются коды ASCII для соответствующих символов, но допускается ввод прописных, строчных или смешанных букв.

f(char*s){char*p=s+strlen(s),b=0;for(;*s;b&=6)b|=*--p^*s++^4;return!b;}

Этот код поддерживает два указателя sи pпересекает строку в противоположных направлениях. На каждом шаге мы сравниваем соответствующие символы, устанавливая btrue, если они не совпадают. Соответствие основано на XOR значений символов:

'A' ^ 'T' = 10101
'C' ^ 'G' = 00100

'C' ^ 'T' = 10111
'G' ^ 'A' = 00110
'A' ^ 'C' = 00010
'T' ^ 'G' = 10011
 x  ^  x  = 00000

В приведенной выше таблице видно, что мы хотим записать успех xx10xили неудачу для чего-либо еще, поэтому мы XOR с 00100(четыре) и маскируем с 00110(шесть), чтобы получить ноль для ATили CGненулевое значение в противном случае. Наконец, мы возвращаем true, если все пары накопили нулевой результат вb противном случае - false.

Тестовая программа:

#include <stdio.h>
int main(int argc, char **argv)
{
    while (*++argv)
        printf("%s = %s\n", *argv, f(*argv)?"true":"false");
}

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.