最短最长公共子序列码


11

解决SLCSC问题的任务是,找到可能的最短代码以解决最长公共子序列问题

给LCS问题的一个有效的解决方案为两个或多个字符串小号1,内容S Ñ是任意字符串Ť使得字符最大长度的Ť出现在所有s ^ ,以相同的顺序在Ť

注意,T不必是S i的子字符串

字符串axbyczxaybzc具有8个常见的长度为3的子序列:

abc abz ayc ayz xbc xbz xyc xyz

这些中的任何一个都是解决LCS问题的有效解决方案。

细节

如上所述,编写程序或函数以解决LCS问题,并遵守以下规则:

  • 输入将包含两个或更多个仅包含小写字母的字符串。

    您可以将这些字符串读取为字符串数组,也可以将单个字符串与您选择的分隔符一起读取。

  • 您的代码必须输出该问题的任何一种可能的解决方案,可以选择后跟换行符或用引号引起来。

  • 您可以假设字符串少于1000个字符,并且最多有20个字符串。

    在这些限制内,您的代码应能在理论上按预期工作(给定无限的时间和内存)。

  • 您的代码必须在我的机器(Intel Core i7-3770,16 GiB RAM)上不到一个小时的时间内完成下一部分的组合测试用例。

    仅对所有可能的子序列进行迭代的方法将不符合时间限制。

  • LongestCommonSequence不允许使用琐碎此任务的内置程序,例如。

适用标准规则。

测试用例

a
ab

输出: a


aaaaxbbbb
bbbbxcccc
ccccxaaaa

输出: x


hxbecqibzpyqhosszypghkdddykjfjcajnwfmtfhqcpavqrtoipocijrmqpgzoufkjkyurczxuhkcpehbhpsuieqgjcepuhbpronmlrcgvipvibhuyqndbjbrrzpqbdegmqgjliclcgahxoouqxqpujsyfwbdthteidvigudsuoznykkzskspjufgkhaxorbrdvgodlb
qnnprxqpnafnhekcxljyysobbpyhynvolgtrntqtjpxpchqwgtkpxxvmwwcohxplsailheuzhkbtayvmxnttycdkbdvryjkfhshulptkuarqwuidrnjsydftsyhuueebnrjvkfvhqmyrclehcwethsqzcyfvyohzskvgttggndmdvdgollryqoswviqurrqhiqrqtyrl

输出:hxbbpyhogntqppcqgkxchpsieuhbncvpuqndbjqmclchqyfttdvgoysuhrrl或其他任何相同长度的公共子序列


riikscwpvsbxrvkpymvbbpmwclizxlhihiubycxmxwviuajdzoonjpkgiuiulbjdpkztsqznhbjhymwzkutmkkkhirryabricvhb
jnrzutfnbqhbaueicsvltalvqoorafnadevojjcsnlftoskhntashydksoheydbeuwlswdzivxnvcrxbgxmquvdnfairsppodznm
kzimnldhqklxyezcuyjaiasaeslicegmnwfavllanoolkhvqkjdvxrsjapqqwnrplcwqginwinktxnkfcuuvoyntrqwwucarfvjg

输出:icsvllvjnlktywuar或其他任何相同长度的公共子序列


rblartqkfggrjpiipuzzypkycnyckkcqceeujiyy
yfpnedyjtiwrhyuktripdwyvgkblzodeufkelhub
ywcyboxwqkibwvredkrbdsyudkulpvmhixeqxynh
bnxwahbzhwjxkotnvbxrojkkldtsodtjmvdrmbgr

输出:krkk或其他任何相同长度的公共子序列


bwfscmotshoczmduwaev
coywaaizdaxjovipsmeh
dobzcpoiedenzlfdjpiu
bbhfeqscvvbwkuoxdoge
drqrzwbcpcsvneodelja

输出:code或其他任何相同长度的公共子序列


nqrualgoedlf
jgqorzglfnpa
fgttvnogldfx
pgostsulyfug
sgnhoyjlnfvr
wdttgkolfkbt

输出:golf或其他任何相同长度的公共子序列


epopyfuhgowedpiqpgfj
ddxyestqynpwmytxhozm
ptubgzyqqksieoovuezv
tovotqmnhgzttfpywjgr
aomvytkgaijlgqzkljls
vzvxpaixrnprxvenbbuo
syfuwxlpcyontqlmfvib
arxleuomstkjegpduwcx
xgqrxaopouvkvwgbzewn
yggunddigctgpnuztzai
izroomywwztdymqszsuo
kiawjnxjncdtufhkrjsp

输出:空字符串



@NotthatCharles并非全部。该问题仅给出两个字符串作为输入,并且没有时间限制。现有的所有答案都使用过幼稚的方法,这些方法的速度太慢而无法遵守该问题的规则。
丹尼斯

最后一个示例可能需要花费最长的时间来计算,但是通过首先删除每个字符串中未出现的每个字符,输出空字符串很简单。您是否可以添加另一个示例,该示例具有相同数量的字符串和字符串长度,其中每个字符串中都使用每个字符,并且LCS至少为5个字符左右?像这样的东西:ghostbin.com/paste/x9caq
Tyilo

@Tylio如果字符串没有更多的通用字符,则合并一些可以提前结束递归的逻辑,这几乎与最后一个测试用例有关。
丹尼斯

@Dennis因此,解决方案不应该能够在合理的时间内使用20个任意长度的1000个字符串运行?
Tyilo

Answers:


4

CJam,31岁

q~L{_:&\f{_2$f#:).>j+}{,}$W>s}j

在线尝试

感谢丹尼斯(Dennis),让9个字节打高尔夫球!

说明:

该算法尝试在子序列的第一个位置使用每个可能的字符,在该字符首次出现后用子字符串替换每个字符串,然后递归调用自身(带有备注)。

q~          read and evaluate the input (taken as an array)
L{…}j       execute block with recursive memoization and no starting values
  _         duplicate the array of strings
  :&\       intersect the strings as character sets and move before the array
             these are all the possible characters for the sequence
  f{…}      for each character and the array
    _2$     duplicate the array and the character
    f#      find the character position in each string
    :)      increment the positions (to skip the character)
    .>      slice each string starting at the corresponding position
    j       call the j block recursively
    +       concatenate the starting character with the result
  {,}$      sort resulting strings (one for each character) by length
  W>        keep only the last element, if any
  s         convert (from 0/1-string array) to string

5

蟒蛇- 665 644

缩进级别:

1: space
2: tab
3: tab + space
4: 2 tabs
5: 2 tabs + space

该代码定义了一个函数o,该函数将字符串列表作为参数,并返回字符串的LCS之一。

def o(t):
 t=[[y for y in x if y in reduce(lambda x,y:x.intersection(y),t,set(t[0]))]for x in t];l=map(len,t);C=[0]*reduce(lambda x,y:x*-~y,l,1);y=lambda z:[x-1for x in z];m=len(t);e=enumerate
 def g(h):
    r,x=0,1
    for k,j in e(h):r+=-~j*x;x*=-~l[k]
    return r
 def f(h):
    i=len(h)
    if i==m:
     b=g(h);c=t[0][h[0]]
     for k,j in e(h):
         if t[k][j]!=c:break
     else:C[b]=1+C[g(y(h))];return
     r=0
     for k,_ in e(h):a=h[:];a[k]-=1;r=max(r,C[g(a)])
     C[b]=r;return
    for j,_ in e(t[i]):f(h+[j])
 def p(h):
    if min(h)==-1:return''
    v=C[g(h)]
    for k,_ in e(h):
        a=h[:];a[k]-=1
        if v==C[g(a)]:return p(a)
    return p(y(h))+t[0][h[0]]
 f([]);return p(y(l))

测试代码:

tests = [
"""
a
ab
""",
"""
aaaaxbbbb
bbbbxcccc
ccccxaaaa
""",
"""
hxbecqibzpyqhosszypghkdddykjfjcajnwfmtfhqcpavqrtoipocijrmqpgzoufkjkyurczxuhkcpehbhpsuieqgjcepuhbpronmlrcgvipvibhuyqndbjbrrzpqbdegmqgjliclcgahxoouqxqpujsyfwbdthteidvigudsuoznykkzskspjufgkhaxorbrdvgodlb
qnnprxqpnafnhekcxljyysobbpyhynvolgtrntqtjpxpchqwgtkpxxvmwwcohxplsailheuzhkbtayvmxnttycdkbdvryjkfhshulptkuarqwuidrnjsydftsyhuueebnrjvkfvhqmyrclehcwethsqzcyfvyohzskvgttggndmdvdgollryqoswviqurrqhiqrqtyrl
""",
"""
riikscwpvsbxrvkpymvbbpmwclizxlhihiubycxmxwviuajdzoonjpkgiuiulbjdpkztsqznhbjhymwzkutmkkkhirryabricvhb
jnrzutfnbqhbaueicsvltalvqoorafnadevojjcsnlftoskhntashydksoheydbeuwlswdzivxnvcrxbgxmquvdnfairsppodznm
kzimnldhqklxyezcuyjaiasaeslicegmnwfavllanoolkhvqkjdvxrsjapqqwnrplcwqginwinktxnkfcuuvoyntrqwwucarfvjg
""",
"""
rblartqkfggrjpiipuzzypkycnyckkcqceeujiyy
yfpnedyjtiwrhyuktripdwyvgkblzodeufkelhub
ywcyboxwqkibwvredkrbdsyudkulpvmhixeqxynh
bnxwahbzhwjxkotnvbxrojkkldtsodtjmvdrmbgr
""",
"""
bwfscmotshoczmduwaev
coywaaizdaxjovipsmeh
dobzcpoiedenzlfdjpiu
bbhfeqscvvbwkuoxdoge
drqrzwbcpcsvneodelja
""",
"""
nqrualgoedlf
jgqorzglfnpa
fgttvnogldfx
pgostsulyfug
sgnhoyjlnfvr
wdttgkolfkbt
""",
"""
epopyfuhgowedpiqpgfj
ddxyestqynpwmytxhozm
ptubgzyqqksieoovuezv
tovotqmnhgzttfpywjgr
aomvytkgaijlgqzkljls
vzvxpaixrnprxvenbbuo
syfuwxlpcyontqlmfvib
arxleuomstkjegpduwcx
xgqrxaopouvkvwgbzewn
yggunddigctgpnuztzai
izroomywwztdymqszsuo
kiawjnxjncdtufhkrjsp
"""
]

for s in tests:
 print o(s.strip().split())

在计算机上运行测试所需的时间:

$ time python 52808-shortest-longest-common-subsequence-code-golfed.py
a
x
hecbpyhogntqtpcqgkxchpsieuhbncvhuqndbjqmclchqyfhtdvgoysuhrrl
icsvllvanlktywuar
krkk
code
golf

        9.03 real         8.99 user         0.03 sys

1
您应该添加一个字节以使代码达到666个字节。好金属。\ m /
Alex A.

@AlexA。是的,我还注意到在计数字节时,因为它在最后一行包含换行符。
蒂洛2015年

我认为有一些小改进应该会有所帮助。首先,在任何地方都有(n+1),您可以替换为,-~n以分别节省2个字节。另外,在map与一起使用的任何地方lambda,请考虑改为使用列表推导。例如,map(lambda x:x-1,z)可以通过将其更改为来缩短三个字节[~-x for x in z]
卡德2015年

r,x=r+(j+1)*x,x*(l[k]+1)可以缩短为r+=(j+1)*x;x*=(l[k]+1)。另外,您也不需要,u=...因为u仅在一个地方使用。只需用该代码代替字母即可u
mbomb007

@ Vioz-和mbomb007谢谢。
蒂洛2015年

4

Pyth,59 58 55 35字节

L&@Fb?+yPMbeeb@FeMbeolNmyXJbdP@bdlb

多亏了@isaacg,削减了20个字节!

55字节版本:

DCHR?k!&.AH@FH?+Cm<1dHeeHql{medH1h.MlZ.eC++<Hk]<1b>HhkH

通过更改.U@bZ@F(fold运算符),减少3个字节。

58字节版本:

DCHR?k!&.AH.U@bZH?+Cm<1dHeeHql{medH1h.MlZ.eC++<Hk]<1b>HhkH

通过更改布尔条件的格式来截断一个字节。

59字节版本:

DCHR?k|!.AH!.U@bZH?+Cm<1dHeeHql{medH1h.MlZ.eC++<Hk]<1b>HhkH

太难了!Python一直存在段错误!可以肯定的是,它是某种错误,但是我无法获得一个最小的测试用例。那好吧。

我基于算法掉这一个。很好,除了一个仅用于两个字符串。我不得不对其进行一些微调以使其在更多产品上运行。然后,最后一个测试用例花费的时间太长,因此如果没有更多的通用字符,我必须添加一个额外的检查以退出递归。

它的速度很慢,但是应该少于一个小时(希望如此)。我正在6 GB RAM的Core i3上进行测试,因此您的16 GB Core i7应该可以通过测试。:)

我还利用了Pyth的自动记忆功能,使其速度更快。

编辑:@丹尼斯说它过去了!

要对其进行测试,请添加以下行:

CQ

并通过标准输入(例如['a', 'ab'])为其提供字符串列表。

35字节版本的说明:

在制品。

55字节版本的说明:

DCH                                                        define a function C that takes a list of strings H
   R                                                       return the following expression
    ?                                                      if
      !&.AH@FH                                             there are no more common letters OR all the strings are empty
     k                                                     return the empty string
              ?          ql{medH1                          else if the last character of every string is equal
               +Cm<1dHeeH                                  return the result of adding the last character to recursion with every item without its last character
                                 h.MlZ.eC++<Hk]<1b>HhkH    otherwise, return the largest result of recursing len(H) times, each time with one element's last character cut off

@丹尼斯 我会努力的。
kirbyfan64sos

@Dennis更新。您现在可以重试。
kirbyfan64sos

最后一个测试用例立即完成。
丹尼斯

@丹尼斯·YESSSSS !!
kirbyfan64sos

@ kirbyfan64sos关于段错误:递归深度过高(例如无限递归)时,Pyth段错误。
isaacg 2015年

4

C,618个 564字节

d,M,N,A[9999][2];char*(R[9999][20]),b[1000];L(char**s,n){char*j[20],c,a=0;int x[n],y=n-1,z,i,t,m=0,w=1;for(;y;)x[y--]=999;for(;y<N;y++){for(i=0;i<n&&s[i]==R[y][i];i++);if(i/n){a=A[y][0];m=A[y][1];w=0;if(m+d<M||!a)goto J;else{c=a;goto K;}}}for(c=97;w&&c<'{';c++){K:t=1,y=1,z=1;for(i=0;i<n;j[i++]++){for(j[i]=s[i];*j[i]-c;j[i]++)t&=!!*j[i];y&=j[i]-s[i]>x[i]?z=0,1:0;}t&=!y;I:if(t){if(z)for(i=0;i<n;i++)x[i]=j[i]-s[i];d++,t+=L(j,n),d--,m=t>m?a=c,t:m;}}if(w){for(y=0;y<n;y++)R[N][y]=s[y];A[N][0]=a;A[N++][1]=m;}J:if(d+m>=M)M=d+m,b[d]=a;if(!d)N=0,M=0,puts(b);return m;}

此处针对“可读性”进行了阐述:

d,M,N,A[9999][2];
char*(R[9999][20]),b[1000];
L(char**s,n){
    char*j[20],c,a=0;
    int x[n],y=n-1,z,i,t,m=0,w=1;
    for(;y;)
        x[y--]=999;
    for(;y<N;y++){
        for(i=0;i<n&&s[i]==R[y][i];i++);
        if(i/n){
            a=A[y][0];
            m=A[y][1];
            w=0;
            if(m+d<M||!a)
                goto J;
            else{
                c=a;
                goto K;
            }
        }
    }
    for(c=97;w&&c<'{';c++){
        K:
        t=1,
        y=1,
        z=1;
        for(i=0;i<n;j[i++]++){
            for(j[i]=s[i];*j[i]-c;j[i]++)
                t&=!!*j[i];
            y&=j[i]-s[i]>x[i]?z=0,1:0;
        }
        t&=!y;
        I:
        if(t){
            if(z)
                for(i=0;i<n;i++)
                    x[i]=j[i]-s[i];
            d++,
            t+=L(j,n),
            d--,
            m=t>m?a=c,t:m;
        }
    }
    if(w){
        for(y=0;y<n;y++)R[N][y]=s[y];
        A[N][0]=a;
        A[N++][1]=m;
    }
    J:
    if(d+m>=M)
        M=d+m,b[d]=a;
    if(!d)
        N=0,M=0,puts(b);
    return m;
}

女士们,先生们,我犯了一个可怕的错误。它使用是漂亮......和GOTO少......至少现在它是

我们定义了一个递归函数L,该函数将s字符数组和n字符串数作为输入。函数将结果字符串输出到stdout,并附带返回该字符串的大小(以字符为单位)。

该方法

尽管代码很复杂,但是这里的策略并不是太复杂。我们从一个相当幼稚的递归算法开始,我将用伪代码来描述它:

Function L (array of strings s, number of strings n), returns length:

Create array of strings j of size n;

For each character c in "a-z",
    For each integer i less than n,
         Set the i'th string of j to the i'th string of s, starting at the first appearance of c in s[i]. (e.g. j[i][0] == c)
         If c does not occur in the i'th string of s, continue on to the next c.
    end For

    new_length := L( j, n ) + 1; // (C) t = new_length
    if new_length > best_length
        best_character := c; // (C) a = best_character
        best_length := new_length; // (C) m = best_length
    end if
end For

// (C) d = current_depth_in_recursion_tree
if best_length + current_depth_in_recursion_tree >= best_found
     prepend best_character to output_string // (C) b = output_string
     // (C) M = best_found, which represents the longest common substring found at any given point in the execution.
     best_found = best_length + current_depth;
end if

if current_depth_in_recursion_tree == 0
    reset all variables, print output_string
end if 

return best_length

现在,该算法本身非常糟糕(但我发现它可以容纳约230个字节)。这不是获得快速结果的方式。该算法在字符串长度方面的缩放比例非常差。但是,此算法使用大量字符串时确实可以很好地扩展。最后一个测试用例几乎可以立即解决,因为其中没有字符串s有任何c共同的字符。我在上面实现了两个主要技巧,从而使速度得到了惊人的提高:

  • 在对的每次调用时L,请检查之前是否都得到了相同的输入。由于实际上信息是通过指针传递给同一组字符串的,因此实际上我们不必比较字符串,而只是比较位置,这很棒。如果我们发现之前已经获得了这些信息,则无需遍历所有计算(大多数情况下,但是获取输出会使此过程变得更加复杂),并且我们只需返回长度即可。如果我们没有找到匹配,保存这组输入/输出进行比较,以未来的呼叫。在C代码中,第二个for循环尝试查找与输入匹配的内容。已知的输入指针保存在中R,相应的长度和字符输出值存储在中A。该计划对运行时产生了巨大影响,尤其是对于较长的字符串。

  • 每当我们找到cin 的位置时s,就有机会立即知道我们发现的不是最佳的。如果的每个位置都c出现另一个字母的某个已知位置之后,我们会自动知道c不会导致最佳子字符串,因为您可以在其中再加上一个字母。这意味着只需很小的成本,我们就可以删除L大型字符串的数百个调用。在上面的C代码中,y如果我们自动知道该字符导致次优字符串,z则设置一个标志;如果我们发现一个字符的出现比任何其他已知字符都早的字符,则设置一个标志。当前最早出现的字符存储在x。该想法的当前实现有些混乱,但在许多情况下性能几乎提高了一倍。

有了这两个主意,一个小时之内无法完成的工作大约花费了0.015秒。

可能还有更多的小技巧可以提高性能,但是这时我开始担心自己打高尔夫球的能力。我仍然对高尔夫不满意,所以我稍后可能会再谈!

时机

这是一些测试代码,我邀请您在线尝试:

#include "stdio.h"
#include "time.h"

#define SIZE_ARRAY(x) (sizeof(x) / sizeof(*x))

int main(int argc, char** argv) {
    /* Our test case */
    char* test7[] = {
        "nqrualgoedlf",
        "jgqorzglfnpa",
        "fgttvnogldfx",
        "pgostsulyfug",
        "sgnhoyjlnfvr",
        "wdttgkolfkbt"
    };

    printf("Test 7:\n\t");
    clock_t start = clock();

    /* The call to L */
    int size = L(test7, SIZE_ARRAY(test7));


    double dt = ((double)(clock() - start)) / CLOCKS_PER_SEC;
    printf("\tSize: %d\n", size);
    printf("\tElapsed time: %lf s\n", dt);

    return 0;
}

我在配备1.7 GHz Intel Core i7芯片且优化设置为的笔记本电脑上运行了OP的测试用例-Ofast。仿真报告需要712KB的峰值。这是每个测试用例的运行示例,并带有计时:

Test 1:
    a
    Size: 1
    Elapsed time: 0.000020 s
Test 2:
    x
    Size: 1
    Elapsed time: 0.000017 s
Test 3:
    hecbpyhogntqppcqgkxchpsieuhbmcbhuqdjbrqmclchqyfhtdvdoysuhrrl
    Size: 60
    Elapsed time: 0.054547 s
Test 4:
    ihicvaoodsnktkrar
    Size: 17
    Elapsed time: 0.007459 s
Test 5:
    krkk
    Size: 4
    Elapsed time: 0.000051 s
Test 6:
    code
    Size: 4
    Elapsed time: 0.000045 s
Test 7:
    golf
    Size: 4
    Elapsed time: 0.000040 s
Test 8:

    Size: 0
    Elapsed time: 0.000029 s


Total time: 0.062293 s

在高尔夫运动中,我的表现相当出色,并且由于人们似乎喜欢我以前的618字节解决方案的蛮力速度(完成所有测试用例需要0.013624 s),因此在这里我将其留作参考:

d,M,N,A[9999][2];char*(R[9999][20]),b[1000];L(char**s,n){char*j[20],c,a=0;int x[n],y,z,i,t,m=0,w=1;for(y=0;y<n;y++)x[y]=999;for(y=0;y<N;y++){for(i=0;i<n;i++)if(s[i]!=R[y][i])break;if(i==n){a=A[y][0];m=A[y][1];w=0;if(m+d<M||!a)goto J;else{c=a;goto K;}}}for(c=97;w&&c<'{';c++){K:t=1,y=1,z=1;for(i=0;i<n;j[i++]++){for(j[i]=s[i];*j[i]-c;j[i]++)if(!*j[i]){t=0;goto I;}if(j[i]-s[i]>x[i])z=0;if(j[i]-s[i]<x[i])y=0;}if(y){t=0;}I:if(t){if(z){for(i=0;i<n;i++){x[i]=j[i]-s[i];}}d++,t+=L(j,n),d--,m=t>m?(a=c),t:m;}}if(w){for(y=0;y<n;y++)R[N][y]=s[y];A[N][0]=a;A[N++][1]=m;}J:if(d+m>=M)M=d+m,b[d]=a;if(!d)N=0,M=0,puts(b);return m;}

该算法本身并没有改变,但是新代码依赖于除法和一些棘手的按位运算,最终使整个过程变慢。


我当时正在考虑发布有关类似主题的最快代码挑战,但是看起来我不必再这样做了。0.01s和712KB令人惊讶。
丹尼斯

这简直太神奇了!
kirbyfan64sos

查看您的解释,到底是best_found什么?它仅被提及两次,一次是在有条件的情况下使用,另一次是在重置时。
kirbyfan64sos

查看C源代码,似乎将best_found其设置为best_length + current_depth。您可能应该在解释中提到它!
kirbyfan64sos

@ kirbyfan64sos best_found是一个全局整数,它描述在执行的任何给定点找到的最长公共子字符串的长度。我将其放在解释中!
BrainSteel 2015年

1

Python 2,285

码:

import re
def f(s,a,b):
  if b==[]:return s+f('',[],a)
  if a==[]:return s+max([f(b[0][i],[b[0][i+1:]],b[1:]) for i in range(len(b[0]))],key=len) if b[0]!='' else ''
  return max([f(s,a+[b[0][i.start()+1:]],b[1:]) for i in re.finditer(s[-1],b[0])],key=len) if ~b[0].find(s[-1]) else ''

用法:

print f('',[],['axbycz','xaybzc'])

说明:

这是一个递归函数。s是我们要寻找的角色。a包含在之后切成的字符串列表sb包含尚未使用的字符串列表。f返回最长的通用字符串。

第一个条件检查我们是否完成了所有字符串的遍历。如果是这样,则表示s是普通字符,然后返回s并寻找更多普通字符。

第二个条件检查我们是否不开始输入任何字符串,这意味着我们甚至没有一个字符(a==[]等价于s=='')。如果是这样,我们将检查中第一个字符串的每个字符b

最后一行移动第一字符串中ba,通过找出在每次出现时s在此字符串。

在第一次调用时,s应为空字符串。a应该为空列表,并且b应包含所有字符串。


2
您应该使用默认参数,这样仅字符串需要提供给函数,例如f(b,s='',a=[])
feersum
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.