Is this n-speak?

33

4

Inspired by Is it double speak?, I devised a harder challenge. Given a string, determine if the string is n-speak, for any \$n\geq 2\$.

N-speak is defined by repeating each letter \$n\$ times. With \$n = 4\$, the string Hello is transformed to HHHHeeeelllllllloooo. Your goal is to figure out if the input is a valid output for any n-speak transformation.

It should be noted that any sentence which is valid n-speak, for \$n = 2k\$, is also valid k-speak. Thus, the hard parts to solve will be odd values of \$n\$.

Input

A string consisting of at least 2 characters. Input could also be a list of characters. Input is case sensitive.

Output

Truthy if the string is n-speak, falsey otherwise.

Examples

True cases

HHeelllloo,,  wwoorrlldd!!
TTTrrriiipppllleee   ssspppeeeaaakkk
QQQQuuuuaaaaddddrrrruuuupppplllleeee    ssssppppeeeeaaaakkkk
7777777-------ssssssspppppppeeeeeeeaaaaaaakkkkkkk
999999999
aaaabb
aaaaaaaabbbbcc
aaaaabbbbb
@@@

If you want to generate additional truthy cases, you can use this MathGolf script. Place the string within the quotation marks, and the value of \$n\$ as the input.

False cases

Hello, world!
TTTrrriiipppllleee   speak
aaaaaaaaaaaaaaaab
Ddoouubbllee  ssppeeaakk
aabbab
aaaabbb
a (does not need to be handled)
(empty string, does not need to be handled)

Of course, since this is code golf, get ready to trim some bytes!

maxb

Posted 2019-08-12T10:03:27.120

Reputation: 5 754

Suggested test case: aabbab – Adám – 2019-08-12T15:22:50.063

Suggested test case: aaaabbb – 640KB – 2019-08-12T15:37:03.457

I'll add them both tomorrow, good suggestions. – maxb – 2019-08-12T21:12:48.767

4I am genuinely honoured and flattered that you have used and expanded my challenge :) – AJFaraday – 2019-08-13T09:46:14.317

@AJFaraday glad that you liked it! I enjoyed both of your challenges, which gave me the idea for this one. There might be an even harder challenge coming soon. – maxb – 2019-08-13T10:25:13.243

@maxb by all means. Although I find that relatively simple challenges often get more response. Just think to yourself "If I saw this, would I automatically start thinking about how to code it". Those are the winners :) – AJFaraday – 2019-08-13T10:29:00.403

When is someone going to post "n-speak"? ;) – dana – 2019-08-14T02:23:05.630

"It should be noted that any sentence which is valid n-speak, for n=2k, is also valid k-speak. Thus, the hard parts to solve will be odd values of n." This is an interesting claim since your examples are $n=2$ and $n=4$ which can't be reduced to odd values. – JiK – 2019-08-14T15:17:32.410

@JiK the 2nd, 4th, 5th, and 9th test cases are all odd numbered. It was mostly an observation that for every even case, a solution to the original Is is double speak? challenge would also would also be correct. – maxb – 2019-08-14T17:57:07.937

Answers

16

APL (Dyalog Unicode), 12 bytes

Runs with ⎕io←0

1≠∨/⍸2≠/∊0⍞0

Try it online!

Golfed together with Adám.

On the input (example: "aaccccaaaaaabb", using "" to denote a string (an array of chars) and '' to denote a char)

∊0⍞0 surround with 0s and flatten, 0 'a' 'a' 'c' 'c' 'c' 'c' 'a' 'a' 'a' 'a' 'a' 'a' 'b' 'b' 0

2≠/ perform pairwise not-equal, 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1

get the 0-indexed indices, 0 2 6 12 14

∨/ compute the GCD, 2

1≠ is this not equal to 1?

user41805

Posted 2019-08-12T10:03:27.120

Reputation: 16 320

10

Java 10, 85 bytes

s->{var r=0>1;for(int i=0;++i<s.length();)r|=s.matches("((.)\\2{"+i+"})*");return r;}

Regex ported from @Arnauld's JavaScript answer.

Try it online.

Explanation:

s->{                          // Method with String parameter and boolean return-type
  var r=0>1;                  //  Result-boolean, starting at false
  for(int i=0;++i<s.length();)//  Loop `i` in the range [1, input-length):
    r|=                       //   Change the result to true if:
      s.matches("((.)\\2{"+i+"})*");
                              //    The input-String matches this regex
                              // NOTE: String#matches implicitly adds a leading ^ and 
                              //       trailing $ to match the full String
  return r;}                  // After the loop, return the result-boolean

Regex explanation:

^((.)\2{i})*$                 // Full regex to match, where `i` is the loop-integer
^           $                 // If the full String matches:
  (.)                         //  A character
     \2{i}                    //  Appended with that same character `i` amount of times
 (        )*                  //  And that repeated zero or more times for the entire string

Kevin Cruijssen

Posted 2019-08-12T10:03:27.120

Reputation: 67 575

8

Jelly, 5 bytes

Œɠg/’

Try it online!

Erik the Outgolfer

Posted 2019-08-12T10:03:27.120

Reputation: 38 134

7

JavaScript (ES6), 53 bytes

Derived from the regular expression used by @wastl in Is it double speak?.

s=>[...s].some((_,n)=>s.match(`^((.)\\2{${++n}})*$`))

Try it online!


Recursive version, 55 bytes

s=>(g=n=>s[++n]&&!!s.match(`^((.)\\2{${n}})*$`)|g(n))``

Try it online!

Commented

s => (                    // s = input string
  g = n =>                // g is a recursive function taking a repetition length n
    s[++n] &&             // increment n; abort if s[n] is not defined
    !!s.match(            // otherwise, test whether s consists of groups of:
      `^((.)\\2{${n}})*$` //   some character, followed by n copies of the same character
    )                     //
    | g(n)                // or whether it works for some greater n
)``                       // initial call to g with n = [''] (zero-ish)

Arnauld

Posted 2019-08-12T10:03:27.120

Reputation: 111 334

7

05AB1E, 5 bytes

γ€g¿≠

Try it online!

Erik the Outgolfer

Posted 2019-08-12T10:03:27.120

Reputation: 38 134

I thought my 14-byter in MathGolf was a good one, but you just crushed it. I'd love an explanation both for this and your Jelly answer. – maxb – 2019-08-12T11:30:03.517

2@maxb It really just takes the lengths of the group runs, calculates their GCD and tests if it's not 1. – Erik the Outgolfer – 2019-08-12T11:30:34.427

6

Python 2, 73 70 69 67 bytes

lambda s:s in[''.join(c*n for c in s[::n])for n in range(2,len(s))]

Try it online!

-4 bytes, thanks to Jitse

TFeld

Posted 2019-08-12T10:03:27.120

Reputation: 19 246

2You can save 3 bytes by replacing set(...) with {...} – Jitse – 2019-08-12T11:06:47.190

1Also, you can remove the space in ...1 in[... – Jitse – 2019-08-12T12:34:33.923

@Jitse Thanks :) – TFeld – 2019-08-12T12:36:12.853

5

Python 3, 69 bytes

lambda s:any(s=="".join(i*k for i in s[::k])for k in range(2,len(s)))

Try it online!

ar4093

Posted 2019-08-12T10:03:27.120

Reputation: 531

5

QuadS, 16 bytesSBCS

1≠∨/⍵
(.)\1*
⊃⍵L

Try it online!

1≠ is 1 different from

∨/ the GCD

 of the result of

(.)\1* PCRE Searching for any character followed by 0 or more repetitions thereof

⊃⍵L and returning the first of the match lengths (i.e. the length of the match)

Adám

Posted 2019-08-12T10:03:27.120

Reputation: 37 779

5

Stax, 5 bytes

╢b}▄;

Run and debug it

Procedure:

  • Calculate run-lengths.
  • GCD of array
  • Is > 1?

recursive

Posted 2019-08-12T10:03:27.120

Reputation: 8 616

4

T-SQL 2008 query, 193 bytes

DECLARE @ varchar(max)='bbbbbbccc';

WITH C as(SELECT number+2n,@ t
FROM spt_values
WHERE'P'=type
UNION ALL 
SELECT n,stuff(t,1,n,'')FROM C
WHERE left(t,n)collate Thai_Bin=replicate(left(t,1),n))SELECT 1+1/~count(*)FROM C
WHERE''=t

Try it online

t-clausen.dk

Posted 2019-08-12T10:03:27.120

Reputation: 2 874

Is "collate Thai_Bin" really necessary? – Dr Y Wit – 2019-08-14T10:39:19.230

1@DrYWit it depends, the database could be set up as case sensitive. But case sensitive databases are not a popular choice. This could be handled better differently using HASHBYTES or maybe VARBINARY, but that is more costly in bytes – t-clausen.dk – 2019-08-14T10:46:58.107

4

PHP, 76 75 bytes

while(($x=strspn($argn,$argn[$n+=$x],$n))>1&&($m=max($m,$x))%$x<1);echo!$x;

Try it online!

First attempt, a somewhat naïve iterative approach.

Ungolfed:

// get the length of the next span of the same char
while( $s = strspn( $argn, $argn[ $n ], $n ) ) {

    // if span is less than 2 chars long, input is not n-speak
    if ( $s < 2 ) {
        break;
    }

    // k is GCD
    $k = max( $k, $s );

    // if span length does not divide evenly into GCD, input is not n-speak
    if( ( $k % $s ) != 0 ) {
        break;
    }

    // increment current input string index
    $n += $s;

}

-1 byte, thx to @Night2!

640KB

Posted 2019-08-12T10:03:27.120

Reputation: 7 149

4

Perl 6, 30 27 26 bytes

{1-[gcd] m:g/(.)$0*/>>.to}

Try it online!

Also uses the GCD trick, but uses the index of the end position of each run matched by the regex. Returns a negative number (truthy) if n-speak, zero (falsey) otherwise.

nwellnhof

Posted 2019-08-12T10:03:27.120

Reputation: 10 037

4

Haskell, 48 bytes

import Data.List
f=(>1).foldr(gcd.length)0.group

Try it online!

Straightforward; uses the GCD trick.

B. Mehta

Posted 2019-08-12T10:03:27.120

Reputation: 763

3

Red, 80 bytes

func[s][repeat n length? s[if parse/case s[any[copy t skip n t]][return on]]off]

Try it online!

More idiomatic Red:

Red, 81 bytes

func[s][any collect[repeat n length? s[keep parse/case s[any[copy t skip n t]]]]]

Try it online!

Galen Ivanov

Posted 2019-08-12T10:03:27.120

Reputation: 13 815

3

Japt , 8 bytes

ò¦ mÊrÕÉ

Try it

ò¦ mÊrÕÉ     :Implicit input of string
ò            :Partition by
 ¦           :  Inequality
   m         :Map
    Ê        :  Length
     r       :Reduce by
      Õ      :  GCD
       É     :Subtract 1
             :Implicit output of boolean negation

Shaggy

Posted 2019-08-12T10:03:27.120

Reputation: 24 623

3

Brachylog, 5 bytes

ġz₂=Ṁ

Try it online!

Takes input through the input variable and outputs through success or failure.

At first I thought this would actually be shorter than my solution to Is it double speak?, but then I realized that ġ can and will try a group length of 1.

ġ        It is possible to split the input into chunks of similar length
 z₂      such that they have strictly equal length, and zipped together
    Ṁ    there are multiple results
   =     which are all equal.

Unrelated String

Posted 2019-08-12T10:03:27.120

Reputation: 5 300

3

Kotlin, 78 bytes

{s->(2..s.length/2).any{i->s.chunked(i).all{z->z.length==i&&z.all{z[0]==it}}}}

Try it online!

Explanation

{s->                      Take a string as input
  (2..s.length/2)         The each string needs two parts at least, prevents the case "aaa" is 3-speak
    .any{i->              If there is any n (in this case i) that is n-speak return true
      s.chunked(i)        Split into length i substrings
      .all{z->            All substrings z
        z.length==i       Should be completely full, ie. "aaa"->["aa","a"]
        &&                And
        z.all{            All chars (it)
          z[0]==it        Should be the same as the first char
        }
      }
    }
  }

Brojowski

Posted 2019-08-12T10:03:27.120

Reputation: 141

Perhaps the description is unclear, but "aaa" is valid 3-speak. The input string should have at least two characters, but they do not need to be different. – maxb – 2019-08-13T05:04:02.033

@maxb, ok cool. That should be -2 bytes. Thanks for the update. I'll fix that tomorrow – Brojowski – 2019-08-13T05:08:32.310

3

Scala, 80 bytes

s=>"(.)\\1*".r.findAllIn(s).map(_.size).reduce((x,y)=>(BigInt(x) gcd y).toInt)>1

Try it online!

PS. Original solution was based on split function but it's longer (83 bytes).

s=>(s+s).split("(.)(?!\\1)").map(_.size+1).reduce((x,y)=>(BigInt(x) gcd y).toInt)>1

Dr Y Wit

Posted 2019-08-12T10:03:27.120

Reputation: 511

This returns true for input aab, unfortunately. – maxb – 2019-08-13T13:36:29.407

@maxb, thanks for checking. s. replaced with (s+s). to handle that. – Dr Y Wit – 2019-08-13T15:33:05.397

Good job! Though now I noticed that it fails for aaaabb and aabbbb. – maxb – 2019-08-13T16:57:57.543

@maxb, apologies, now I tested on all your test cases from starting post. – Dr Y Wit – 2019-08-14T00:09:18.047

2

Wolfram Language (Mathematica), 34 bytes

GCD@@Length/@Split@Characters@#>1&

Try it online!

Roman

Posted 2019-08-12T10:03:27.120

Reputation: 1 190

2

Perl 5 -p, 83 79 76 74 bytes

$_=s,(.)\1+,$t=length$&;$t/=2while$t%2-1;$r+=$t==($g||=$t);'',ge==$r&&/^$/

Try it online!

Xcali

Posted 2019-08-12T10:03:27.120

Reputation: 7 671

2

Brain-Flak, 96 bytes

{<>({}())<>({}[({})]){{}<>({}<>){{(({})){({}[()])<>}{}}<>([{}()]({}<>)<>)}(<>)<>}{}}<>{}({}[()])

Try it online!

Uses the same GCD trick that many other submissions use. Output is 0 if the input is not n-speak, and a positive integer otherwise.

# For each character in the input
{

  # Add 1 to current run length
  <>({}())<>

  # If current and next characters differ:
  ({}[({})]){

    # Clean up unneeded difference
    {}<>

    # Move current run length to left stack, exposing current GCD on right stack
    ({}<>)

    # GCD routine: repeat until L=0
    {

      # Compute L mod R
      {(({})){({}[()])<>}{}}<>

      # Move R to left stack; finish computing L mod R and push to right stack
      ([{}()]({}<>)<>)

    }

    # Push 0 for new run length
    (<>)<>

  }{}

}

# Output GCD-1
<>{}({}[()])

Nitrodon

Posted 2019-08-12T10:03:27.120

Reputation: 9 181

2

Oracle SQL, 182 bytes

select+1-sign(min(length(x)-(select sum(length(regexp_substr(x,'(.)\1{'||i||'}',1,level)))from t connect by level<length(x))))from(select x,level i from t connect by level<length(x))

It works with an assumption that input data is stored in a table t(x), e.g.

with t(x) as (select 'HHeelllloo,,  wwoorrlldd!!' from dual)

Dr Y Wit

Posted 2019-08-12T10:03:27.120

Reputation: 511

2

K (ngn/k), 29 23 bytes

{~|/(&/s@&1<s)!s:#'=:x}

Try it online!

edit: removed some unnecessary colons (i know when a monadic is required but it's not always clear to me if there's ambiguity so i default to including the colon) and changed the mod x-y*x%y to ngn/k's y!x, which meant i could remove a variable assignment

scrawl

Posted 2019-08-12T10:03:27.120

Reputation: 1 079

1

APL (Dyalog Unicode), 24 22 bytesSBCS

Anonymous tacit prefix function.

⊂∊1↓⍳∘≢{⍵/⍨(≢⍵)⍴⍺↑⍺}¨⊂

Try it online!

 enclose the string to treat map using the entire string
 e.g. "aaabbb"

⍳∘≢{ for each of the ɩndices 1 through the tally of characters in the string:
 e.g. 3

⍺↑⍺ take the current number of elements from the current number, padding with 0s
 e.g. [3,0,0]

(≢⍵)⍴ cyclically reshape into the shape of the tally of characters in the string
  e.g. [3,0,0,3,0,0]

⍵/⍨ use that to replicate the string's characters
  "aaabbb"

1↓ drop the first one (n = 1)

⊂∊ is the the entire string a member of that list?

Adám

Posted 2019-08-12T10:03:27.120

Reputation: 37 779

Are you dividing the input string into n-sized chunks, and checking that all characters are equal within each chunk? I haven't gotten into APL, but it's definitely the most readable "golfing" language. – maxb – 2019-08-12T10:57:42.920

@maxb I'm in the process of writing an explanation. I'm filtering with all possible masks [1,0,0,1,0,0…] etc. I'll be happy to teach you APL (it doesn't take long to learn). Just pop unto the APL Orchard.

– Adám – 2019-08-12T10:59:24.323

Here's {1<∨/≢¨⍵⊆⍨≢∘∪¨,\⍵} for 18

– user41805 – 2019-08-12T11:17:39.380

@Cowsquack Clever, and different, so why don't you post {1<∨/≢¨⍵⊆⍨≢¨∪\⍵}? – Adám – 2019-08-12T11:23:54.180

Unfortunately it fails for aacccaaaaabb – user41805 – 2019-08-12T11:37:34.473

@Cowsquack 1<∨/≢¨0⊂⍨1,2≠/⍞ then – Adám – 2019-08-12T11:39:48.523

@Cowsquack 1<∨/2-/⍸2≠/∊0⍞0 works too. – Adám – 2019-08-12T11:42:14.007

I suppose at this point you can just post the answer :P – user41805 – 2019-08-12T11:42:37.340

---That (1<∨/2-/⍸2≠/∊0⍞0) is a nice answer btw--- Actually wait it fails for 999999999 but that can be fixed by changing the 1< to 1≠ – user41805 – 2019-08-12T11:43:39.293

Here's 1≠∨/⍸2≠/∊0⍞0 with ⎕io←0 – user41805 – 2019-08-12T11:49:08.390

@Cowsquack OK, you post then (I don't need rep) and we'll call it a collaborative effort :-) Try it online!

– Adám – 2019-08-12T11:50:25.380

1

Japt -mR, 12 bytes

ÊÆóXäd_äe e

Try it

Embodiment of Ignorance

Posted 2019-08-12T10:03:27.120

Reputation: 7 014

1

Retina 0.8.2, 28 bytes

M!`(.)\1*
.
.
^(..+)(\1|¶)*$

Try it online! Link includes test cases. Explanation:

M!`(.)\1*

Split the text into runs of identical characters.

.
.

Replace them all with the same character.

^(..+)(\1|¶)*$

Check whether the GCD of the lengths of the runs is greater than 1.

Neil

Posted 2019-08-12T10:03:27.120

Reputation: 95 035

1

MathGolf, 14 bytes

£─╞möl╠mÅ▀£╙╓┴

Try it online!

Explanation

Checks all possible divisions of the input string into equal length chunks, and checks if there is a partition in which all chunks have just one unique character.

£                length of string with pop
 ─               get divisors
  ╞              discard from left of string/array (removes 1)
   mö            explicit map using 7 operators
     l           push input
      ╠          divide input into chunks of size k
       mÅ        explicit map using 2 operators
         ߜ      number of unique elements of list
           ╙     get maximum number of unique characters per chunk
                 loop ends here
            ╓    get the minimum of all maximums
             ┴   check if equal to 1

maxb

Posted 2019-08-12T10:03:27.120

Reputation: 5 754

1

Gaia, 10 bytes

ẋl¦d¦&⊢⌉1>

Try it online!

Same "GCD of run-lengths > 1" as many other submissions use.

There is a bug in ė (run-length encoding) that drops the last unique element of the list, otherwise we could have the following 9 byte solution: ė(¦d¦&⊢1>.

Giuseppe

Posted 2019-08-12T10:03:27.120

Reputation: 21 077

1

Pyth, 7 bytes

Outputs 0 for falsy inputs or a positive integer otherwise.

tiFhCr8

Try it online!

Mr. Xcoder

Posted 2019-08-12T10:03:27.120

Reputation: 39 774

1

Pyth, 8 bytes

<1iFhMr8

Try it online!

<1iFhMr8Q   Implicit: Q=eval(input())
            Trailing Q inferred
      r8Q   Run length encode Q into [count, character]
    hM      Take first element of each
  iF        Reduce by GCD
<1          Is 1 less than the above? Implicit print

Sok

Posted 2019-08-12T10:03:27.120

Reputation: 5 592

1

Perl 5 -n, 38 bytes

for$i(1..y///c){print/^((.)\2{$i})*$/}

Try it online!

The print"\n" in the footer is needed to separate the outputs.

Straightforward loop through all possible ns. Outputs nothing for "1-speak", anything else for n-speak where n > 1.

wastl

Posted 2019-08-12T10:03:27.120

Reputation: 3 089

1

C# (Visual C# Interactive Compiler), 76 bytes

s=>s.Where((_,n)=>s.Count%(n+=2)<1&!s.Where((c,i)=>c!=s[i/n*n]).Any()).Any()

Try it online!

Generate all n from 2, 3, 4, ... and do the following:

  • Check if the length of input is divisible by n
  • Compare each character of input to the corresponding character of an n-speak string

If both checks pass, the input string is n-speak.

dana

Posted 2019-08-12T10:03:27.120

Reputation: 2 541

1

D , 126 bytes

bool f(string s){import std.algorithm;auto a=s.group.minElement!(a=>a[1])[1];foreach(g;s.group)if(g[1]%a)return 0;return a>1;}

First code golf I've done in D.

Does not handle empty input strings (causes an assertion failure in the standard library).

Ungolfed version:

bool f(string s) {
    import std.algorithm; // for group and minElement

    // splits the string into a groups of the same character
    // eg. "HHHiii".group returns
    // [Tuple!(char, uint)('H', 3), Tuple!(char, uint)('i', 3)]
    auto groups = s.group;

    // gets the smallest group length
    auto min_length = groups.minElement!(a => a[1])[1];

    foreach(g; groups) // for each group
        if(g[1] % min_length) // if it's length is not divisible by smallest length
            return 0; // return false

    return min_length > 1; // return true if smallest length was above 1
}

qookie

Posted 2019-08-12T10:03:27.120

Reputation: 81