String.prototype.isRepeated

41

4

UPDATE : isaacg's Pyth submission is the winner!


Many of you must have heard that there is a cooler version of JavaScript in town (read ES6) which has a method String.prototype.repeat so that you can do

"Hello, World!".repeat(3)

and get

"Hello, World!Hello, World!Hello, World!"

as the output.

Your job is to write a function or a program in a language of your choice which detects if a string has been gone under such transformation.

i.e. The input string can be represented as an exact n times repetition of a smaller string. The output (as function's return statement or STDOUT) should be truthy if the string can be or falsy if the string cannot be represented as a repetition of smaller string.

Some sample input:

"asdfasdfasdf"  // true
"asdfasdfa"     // false
"ĴĴĴĴĴĴĴĴĴ"     // true
"ĴĴĴ123ĴĴĴ123"  // true
"abcdefgh"      // false

Note that the last input is false, thus n should be greater than 1

Complete rules

  • Write a function/program in any language to input (via function argument/command line args/STDIN) a string
  • Return/Print truthy value if the given string is formed via an exact repetition of a smaller string, repeating at least twice.
  • Maximum size of the input string is ideally Infinity
  • String can have all possible ASCII characters
  • This is a so smallest code in characters wins.

Optimizer

Posted 2014-09-15T20:48:42.467

Reputation: 25 836

What should "" - the empty string - return? (It contains an infinite number of copies of the empty string.) – billpg – 2014-09-17T13:29:00.587

@billpg falsy value – Optimizer – 2014-09-17T13:42:39.630

Are you tie-breaking by votes? The common practice is earlier submission I think (well, the first one that got golfed down to the tying score). But I'm not sure that's written down as the default tie-breaker anywhere, so ultimately it's up to you. – Martin Ender – 2014-09-17T16:54:28.193

Time between their posting is only 30 minutes. I will not consider that to be enough for winning :) . Since that time won't change now, but votes can, I went with votes – Optimizer – 2014-09-17T17:03:27.997

This question should be renamed into xnor :) He is the man! – Silviu Burcea – 2014-09-19T08:52:11.037

Answers

16

Pyth, 9

/:+zz1_1z

Or

}z:+zz1_1

These are both close translations of @xnor's python answer, except that they take input from STDIN and print it. The first is equivalent to:

z = input()
print((z+z)[1:-1].count(z))

0 for False, 1 for True.

The second line is equivalent to:

z = input()
print(z in (z+z)[1:-1])

False for False, True for True.

Pyth's official compiler had a bug related to the second one, which I just patched, so the first is my official submission.

isaacg

Posted 2014-09-15T20:48:42.467

Reputation: 39 268

I was just searching for a way to inform you about that bug (the Boolean doesn't get printed). Didn't think of the first and using x was too long... – Dennis – 2014-09-16T00:13:27.070

Yeah, the bug is fixed now. Also, if you want to report bugs, a good way might be to open an issue on the github site, here: https://github.com/isaacg1/pyth/issues

– isaacg – 2014-09-16T00:31:29.850

Oh, there it is. I don't know my way around GitHub, and I never noticed the navigation panel on the right... – Dennis – 2014-09-16T00:33:36.667

81

Python (24)

lambda s:s in(s+s)[1:-1]

Checks if the string is a substring of itself concatenated twice, eliminating the first and last characters to avoid trivial matches. If it is, it must be a nontrivial cyclic permutation of itself, and thus the sum of repeated segments.

xnor

Posted 2014-09-15T20:48:42.467

Reputation: 115 687

Oh dear, that's so much better than my trivial solution... – Falko – 2014-09-15T21:17:20.653

8A trivial translation into Golfscript yields 10 chars: ..+);(;\?) – Justin – 2014-09-15T22:49:46.300

3I don't quite understand how this works. Can you give a manually explained example of how this would handle a string? – Nzall – 2014-09-16T08:37:40.410

8@NateKerkhofs take abcabc. s+s turns it into abcabcabcabc. the [1:-1] chops of the two ends to yield bcabcabcabcab. and then s in ... tries to find abcabc as a substring of that. This substring can't be found in either of the original half, because they have both been shortened, so it must span both halves. In particular, it must have its own end before its start, which implies that it must be made up of identical (repeated) substrings. – Martin Ender – 2014-09-16T09:26:20.820

Won't this fail on two character strings such as ab? – britishtea – 2014-09-16T16:48:19.723

6You chop it after you double it. ab becomes abab becomes ba, so it returns false, while aa becomes aaaa becomes aa, which returns true. – histocrat – 2014-09-16T16:53:37.637

(how) will it work with 3 copies? for example, "qweqweqwe" – Display Name – 2014-09-17T14:53:30.107

1@SargeBorsch It works just the same: qweqweqwe in weqweqweqweqweqw is True. – xnor – 2014-09-17T20:24:24.220

30

Regex (ECMAScript flavour), 11 bytes

Sounds like a job for regex!

^([^]+)\1+$

Test it here.

I've chosen ECMAScript, because it's the only flavour (I know) in which [^] matches any character. In all others, I'd either need a flag to change the behaviour of . or use [\s\S] which is three characters longer.

Depending on how we're counting the flag, that could of course be a byte shorter. E.g. if we're counting pattern + flags (e.g. ignoring delimiters), the PCRE/Perl equivalent would be

/^(.+)\1+$/s

Which is 10 bytes, ignoring the delimiters.

Test it here.

This matches only strings which consist of at least two repetitions of some substring.

Here is a full 26-byte ES6 function, but I maintain that regular expression submissions are generally valid:

f=s->/^([^]+)\1+$/.test(s)

Martin Ender

Posted 2014-09-15T20:48:42.467

Reputation: 184 808

@Ajedi32 It can also be done in 10 bytes using \X: ^(\X+)\1+$

– Deadcode – 2019-01-21T20:03:47.950

^(.+)\1+$ works for me, which is 9 bytes. It doesn't work for you ? – Optimizer – 2014-09-16T08:51:44.980

@Optimizer Try a string with line breaks. – Martin Ender – 2014-09-16T08:53:27.793

I tried asd\nasd\nasd\n . It works – Optimizer – 2014-09-16T08:55:08.077

@Optimizer http://refiddle.com/refiddles/5417fb2475622d4df7e70a00 doesn't seem to work for me (and it shouldn't)

– Martin Ender – 2014-09-16T08:56:33.583

Yup, that doesn't work. Maybe it escapes the \ when I write \n manually – Optimizer – 2014-09-16T09:07:43.183

@Optimizer Well, the other way round: that tester doesn't understand escape sequences at all (and why would it), so \n is just two ASCII characters like any other ones. – Martin Ender – 2014-09-16T09:15:49.927

I would expect a regex matcher to not escape strings. When I test in web console or scratchpad, \n is treated as single character. – Optimizer – 2014-09-16T09:18:35.477

@MartinBüttner Good point. I never thought about that. – Toothbrush – 2014-09-16T19:52:59.357

The 10-byte version on regex101.com (complete with explanation): http://regex101.com/r/qZ0jP3/1

– Ajedi32 – 2014-09-16T20:16:52.677

@Ajedi32 oh yeah, that's actually where I tested it. Forgot to add the link. Thanks! – Martin Ender – 2014-09-16T20:22:39.307

12

CJam, 9

q__+)@+#)

Similar to xnor's idea.

q      " Read input. ";
__+    " Duplicate twice and concatenate them together. ";
)      " Remove the last character of the longer string. ";
@+     " Insert that character at the beginning of the shorter string. ";
#)     " Find the shorter string in the longer string, and increase by one. ";

jimmy23013

Posted 2014-09-15T20:48:42.467

Reputation: 34 042

+1 obligated to upvote this ahead of my own CJam answer

– Digital Trauma – 2014-09-15T23:36:02.083

Why the need for the final )? I think its reasonable to have -1 mean FALSE and >=0 mean TRUE – Digital Trauma – 2014-09-15T23:39:22.077

@DigitalTrauma I think 0 is falsy in CJam... for operators like g and ?. – jimmy23013 – 2014-09-15T23:42:57.283

@DigitalTrauma: Whether it's needed ultimately depends on the OP, but strictly speaking only zero is considered falsy in CJam. – Dennis – 2014-09-15T23:43:03.340

@user23013 @Dennis But what about the # find operator? Surely the result of that is also "truthy" from the success vs failure perspective? – Digital Trauma – 2014-09-15T23:45:51.453

@DigitalTrauma: The fact that # returns a truthy value on failure is a real annoyance. Nine times out of ten, I don't need the actual index; I just want to know if there's a match. In a sense, you're right. I guess I've just learned to put up with the extra byte it adds to my answers... – Dennis – 2014-09-15T23:51:19.533

@DigitalTrauma Truthy and falsy usually means the value can be converted to boolean as true or false, which shouldn't be different for different operations. But well, the CJam documentation didn't mention what is truthy or falsy, or anything related to boolean data types. – jimmy23013 – 2014-09-15T23:59:58.807

@Dennis I think this is good discussion, so I've taken it to meta http://meta.codegolf.stackexchange.com/questions/2190/interpretation-of-truthy-falsey

– Digital Trauma – 2014-09-16T00:28:10.980

7

APL, 11

2<+/x⍷,⍨x←⍞

Explanation
takes string input from screen
x← assigns to variable x
,⍨ concatenates the string with itself
x⍷ searches for x in the resulting string. Returns an array consisting of 1's in the starting position of a match and 0's elsewhere.
+/ sums the array
2< check if the sum is greater than 2 (as there will be 2 trivial matches)

TwiNight

Posted 2014-09-15T20:48:42.467

Reputation: 4 187

7

CJam, 10 bytes

I caught the CJam bug. My first answer, so probably can be golfed some more:

q__+(;);\#

Outputs -1 for FALSE and a number >=0 for TRUE

Digital Trauma

Posted 2014-09-15T20:48:42.467

Reputation: 64 644

5Welcome to the club! – Dennis – 2014-09-15T23:36:48.817

5

GolfScript, 10 bytes

..+(;);\?)

Yet another implementation of xnor's clever idea.

Dennis

Posted 2014-09-15T20:48:42.467

Reputation: 196 637

Hahaha, I just posted this a minute ago: http://codegolf.stackexchange.com/questions/37851/string-prototype-isrepeated#comment86713_37855 . I thought about posting it as an answer, but I thought that trivial translations are uninteresting.

– Justin – 2014-09-15T22:53:06.577

I even checked for new answers this time, but not for new comments... Your code is missing the ) though; when there's not match, it will print -1. If you're going to post that as an answer, I'll gladly delete mine. – Dennis – 2014-09-15T22:54:41.150

I added the ) just before you posted your answer (I edited the comment) – Justin – 2014-09-15T22:55:46.057

I don't care about rep or anything, so I'll just let you have the answer ;-). – Justin – 2014-09-15T22:56:54.043

Your call. I've hit the rep cap anyway... :P – Dennis – 2014-09-15T23:07:05.460

1Improved version (in CJam): q__+)@+#). It doesn't work in GolfScript. – jimmy23013 – 2014-09-15T23:19:50.863

1@user23013: Not again. I was just going to post that! There are too many CJammers out there by now... :P – Dennis – 2014-09-15T23:21:01.307

@user23013: You should add that as an answer. – Dennis – 2014-09-15T23:25:21.580

3

Python - 59 57

lambda s:any([s*n==s[:n]*len(s)for n in range(2,len(s))])

Falko

Posted 2014-09-15T20:48:42.467

Reputation: 5 307

3

Pure bash, 30 bytes

Simple port of @xnor's clever answer:

[[ ${1:1}${1:0: -1} =~ "$1" ]]

Exit code is 0 for TRUE and 1 for FALSE:

$ for s in 'Hello, World!Hello, World!Hello, World!' 'asdfasdfasdf' 'asdfasdfa' 'ĴĴĴĴĴĴĴĴĴ' 'ĴĴĴ123ĴĴĴ123' 'abcdefgh'; do echo "./isrepeated.sh "\"$s\"" returns $(./isrepeated.sh "$s"; echo $?)"; done
./isrepeated.sh "Hello, World!Hello, World!Hello, World!" returns 0
./isrepeated.sh "asdfasdfasdf" returns 0
./isrepeated.sh "asdfasdfa" returns 1
./isrepeated.sh "ĴĴĴĴĴĴĴĴĴ" returns 0
./isrepeated.sh "ĴĴĴ123ĴĴĴ123" returns 0
./isrepeated.sh "abcdefgh" returns 1
$ 

Note =~ within [[ ... ]] is the regex operator in bash. However "Any part of the pattern may be quoted to force it to be matched as a string". So as ai often the case with bash, getting quoting right is very important - here we just want to check for a string submatch and not a regex match.

Digital Trauma

Posted 2014-09-15T20:48:42.467

Reputation: 64 644

3

TI-BASIC - 32

I thought I'd try a tokenized language. Run with the string in Ans, returns 0 if false and the length of the repeated string if true.

inString(sub(Ans+Ans,1,2length(Ans)-1),sub(Ans,length(Ans),1)+Ans

Amazing how it's a one-liner.

Josiah Winslow

Posted 2014-09-15T20:48:42.467

Reputation: 725

But... but... I was going to use TI-BASIC :P +1 – Timtech – 2014-09-16T22:57:42.323

@Timtech Well, note to anyone trying string manipulation in TI-BASIC: Don't try string manipulation in TI-BASIC. :P That was so hard to make and optimize. – Josiah Winslow – 2014-09-17T01:18:06.613

Good idea. String manipulation is one of the hardest things to do. However, I've posted several answers like this, so I guess now you have a competitor ;) – Timtech – 2014-09-17T11:03:04.597

Bring it on! :P – Josiah Winslow – 2014-09-17T23:39:29.493

3

ECMAScript 6 (189)

(function(){var S=String.prototype,r=S.repeat;S.isRepeated=function(){return!1};S.repeat=function(c){var s=new String(r.call(this,c));if(c>1)s.isRepeated=function(){return!0};return s}}());

 

< console.log("abc".isRepeated(),"abc".repeat(10).isRepeated());
> false true

Surely this is the only valid solution? For example, the word (string) nana isn't necessarily created from "na".repeat(2)

Mardoxx

Posted 2014-09-15T20:48:42.467

Reputation: 131

"nana" isn't, but the question is not testing whether .repeat was used or not. Rather, whether the string is a repeated one or not – Optimizer – 2014-09-18T11:00:59.290

I know, I was just trying to be a smart-arse :P – Mardoxx – 2014-09-18T11:02:21.620

2

ECMAScript 6 (34 36)

Another ES6 answer, but without using repeat and using xnor's trick:

f=i=>(i+i).slice(1,-1).contains(i)

Must be run in the console of a ES6-capable browser such as Firefox.

Ingo Bürk

Posted 2014-09-15T20:48:42.467

Reputation: 2 674

2

C 85

l,d;f(s){return l=strlen(s),strstr(d,strcpy(strcpy(d=alloca(l*2+1),s)+l,s)-1)-d-l+1;}

It turned out to be quite long but external functions are always like that. It came to my mind that I could rewrite every string function replacing them by a loop or a recursive one. But in my experience it would turn out longer and frankly I don't want to try that out.

After some research I saw solutions on high performance but not as clever (and short) as xnor's one. just to be original... i rewrote the same idea in c.

explanation:

int length, 
    duplicate;
int is_repetition(char *input)
{
    // length = "abc" -> 3
    length = strlen(input);
    // alloca because the function name is as long as "malloc" 
    // but you don't have to call free() because it uses the stack
    // to allocate memory
    // duplicate = x x x x x x + x
    duplicate = alloca(length*2 + 1);
    // duplicate = a b c 0 x x + x
    strcpy(duplicate, input);
    // duplicate = a b c a b c + 0
    strcpy(duplicate + length, input);
    if (strstr(duplicate,duplicate + length - 1) != duplicate + length - 1)
        // repetition
        // e.g. abab -> abababab -> aba[babab]
        // -> first occurence of [babab] is not aba[babab]
        // but a[babab]ab -> this is a repetition
        return 1;
    else
        // not repetition
        // e.g. abc -> abcabc -> ab[cabc]
        // -> first occurence of [cabc] is ab[cabc]
        // it matches the last "cabc"
        return 0;
}

bebe

Posted 2014-09-15T20:48:42.467

Reputation: 3 916

1

Jelly, 3 bytes

ṙJċ

Try it online!

Same as this answer (maybe the later challenge is a generalization of this one?).

Erik the Outgolfer

Posted 2014-09-15T20:48:42.467

Reputation: 38 134

1

ECMAScript 6 (59 62 67 73)

Not a winner, but seems like there should at least be one answer actually in ES6 for this question that actually uses the repeat function:

f=i=>[...i].some((_,j)=>i.slice(0,j).repeat(i.length/j)==i)

Must be run in the console of a ES6-capable browser such as Firefox.

It does a lot of unnecessary iterations, but why make it longer just to avoid that, right?

  • Edit #1: Saved a few bytes by converting it into a function. Thanks to Optimizer!
  • Edit #2: Thanks to hsl for the spread operator trick to save more bytes!
  • Edit #3: And thanks to Rob W. for another 3 bytes!

Ingo Bürk

Posted 2014-09-15T20:48:42.467

Reputation: 2 674

You can just convert it into a function to save more bytes there – Optimizer – 2014-09-16T18:24:12.630

@Optimizer True, I guess it doesn't have to be "stdin". Your live up to your name :) – Ingo Bürk – 2014-09-16T18:27:12.017

I haven't tested this, but you should be able to use the spread operator for [...i] instead of i.split('')

– NinjaBearMonkey – 2014-09-16T18:37:28.540

1@hsl Crazy, that works. I didn't know the spread operator works like that. Originally I desperately tried to use it to create an array with the range 0..N. Thanks! – Ingo Bürk – 2014-09-16T18:39:39.277

1.slice(0,j) is one character shorter than .substr(0,j). Further, the conversion to an integer seems unnecessary |0 can be removed (using |0 actually reduces the usefulness of the method because it will fail for repetitions that exceed 2^31). – Rob W – 2014-09-19T18:08:04.417

@RobW Mh, weird. I added the |0 because I got an error. Doesn't seem to error out now, though, so I guess I really can remove it. Thanks! – Ingo Bürk – 2014-09-19T18:16:53.883

0

Java 8, 28 bytes

s->s.matches("(?s)(.+)\\1+")

Try it online.

Explanation:

Checks if the input-String matches the regex, where String#matches implicitly adds ^...$ to match the entire String.
Explanation of the regex itself:

^(s?)(.+)\1+$
^                Begin of the string
 (s?)            Enable DOTALL-mode, where `.` also matches new-lines
     (           Open capture group 1
      .+          One or more characters
        )        Close capture group 1
         \1+     Plus the match of the capture group 1, one or more times
            $    End of the string

So it basically checks if a substring is repeated two or more times (supporting new-lines).

Kevin Cruijssen

Posted 2014-09-15T20:48:42.467

Reputation: 67 575