# Unique is Cheap

Write a function or program that determines the cost of a given string, where

• the cost of each character equals the number of how many times the character has occurred up to this point in the string, and
• the cost of the string is the sum of its characters' costs.

### Example

For an input of abaacab, the cost is computed as follows:

a b a a c a b
1   2 3   4    occurrence of a
1         2  occurrence of b
1      occurrence of c
1+1+2+3+1+4+2 = 14

Thus the cost for the string abaacab is 14.

### Rules

• The score of your submission is the cost of your code as defined above, that is your submission run on its own source code, with a lower score being better.
• Your submission should work on strings containing printable ASCII-characters, plus all characters used in your submission.
• Characters are case-sensitive, that is a and A are different characters.

### Testcases

input -> output
"abaacab" -> 14
"Programming Puzzles & Code Golf" -> 47
"" -> 0
"       " -> 28
"abcdefg" -> 7
"aA" -> 2

• How do program flags such as -n for Perl count towards the score? It traditionally counts as 1 byte because the edit distance between the standard perl -e and perl -ne is 1, but for this challenge, will the n count for the purposes of counting duplicates? Jun 19, 2017 at 20:20
• @ValueInk Yes, I think counting the n is the fairest option. Jun 19, 2017 at 20:34
• I really wish there was a brainfuck solution to this challenge. Jun 21, 2017 at 12:16
• +1 for The score of your submission is the cost of your code Jun 22, 2017 at 15:21
• cost of a character is defined as how often this character has already occurred in the string, i'd probably changing to how many times the character has occurred up to this point to make it clearer that the first use costs 1, not 0 Jan 19, 2018 at 19:33

# MATL, score 4

&=Rz

Try it online!

### Explanation

Consider input 'ABBA' as an example.

&=   % Implicit input. Matrix of all equality comparisons
% STACK: [1 0 0 1;
0 1 1 0;
0 1 1 0;
1 0 0 1]
R    % Upper triangular part
% STACK: [1 0 0 1;
0 1 1 0;
0 0 1 0;
0 0 0 1]
z    % Number of nonzeros. Implicitly display
% STACK: 6
• Are you a linear algebra professor? Jun 20, 2017 at 18:53
• @carusocomputing Actually a mobile communications professor. My tendency to use matrices comes from years of programming in Matlab Jun 20, 2017 at 18:57
• Neat! Is Matlab big in that area? I've never really looked into GSM or anything like it. Jun 20, 2017 at 19:27
• I joined this community just to commend you on this brilliant solution!
– Wboy
Jun 21, 2017 at 5:21
• @carusocomputing Matlab is a very common tool/language in engineering in general. It's good at numerical computation: linear algebra, signal processing, and the like. And being an interpreted language it's very easy to use Jun 21, 2017 at 8:29

# Python, score 49

lambda S:sum(1+S.count(C)for[C]in	S)/2

Try it online!

There's a tab after in.

Score breakdown:

• +27 for 27 unique chars
• +16 for 8 double chars: ()Camnou
• +6 for 1 tripled char: S
• Use a tab instead of a space to save a byte. Jun 19, 2017 at 17:20
• @mbomb007 Just had the same idea :-)
– xnor
Jun 19, 2017 at 17:21
• @mbomb007 Hah, that's a genius trick :-) Jun 19, 2017 at 17:21
• @mbomb007 that's just tabs vs. spaces war inside golfed code Jun 19, 2017 at 17:23
• I was going to suggest using a form feed (which is also permitted whitespace in Python syntax), but you don't have any more whitespace to replace. Jun 19, 2017 at 23:39

# T-SQL, score 775 579! 580

declaRe @ char(876),@x int,@v int=0Select @=q+CHAR(9)from z X:seleCT @x=len(@),@=REPLACE(@,LEFT(@,1),''),@v+=(@x-LEN(@))*(@x-LEN(@)+1)/2IF LEN(@)>0GOTO X prINT @v-1

EDIT: Dropped a couple of variables, compacted a bit. Down to 16 @ symbols instead of 22, that by itself reduces my score by a whopping 117 points!

Nice contest, I like the requirement to optimize for something besides total character count.

Input is via varchar field q in pre-existing table z, per our IO rules. The database containing this input table must be set to a case-sensitive collation.

Formatted:

declaRe @ char(876), @x int, @v int=0
Select @=q+CHAR(9)from z
X:
seleCT @x=len(@)
,@=REPLACE(@,LEFT(@,1),'')
,@v+=(@x-LEN(@))*(@x-LEN(@)+1)/2
IF LEN(@)>0 GOTO X
prINT @v-1

SQL keywords aren't case sensitive, so I used mixed case to minimize the count of duplicate letters (aaAA generates a better/lower score than aaaa).

The main loop compares the length before and after stripping all instances of the first character out. That difference n*(n+1)/2 is added to a running total.

The SQL LEN() function annoyingly ignores trailing spaces, so I had to append a control character and subtract 1 at the end.

EDIT: Fixed a miscalculation of my own score by 2 points (issue with quoting quotes), reduced by 1 by changing casing of one R. Also working on a completely different strategy, I'll be posting that as its own answer.

• At first I thought your score was 579! ≈ 8.22 x 10^1349 Jul 17, 2017 at 12:54

# C (gcc), score:  113  103 100  96  91

Thanks to @ugoren, @CalculatorFeline, @gastropner, @l4m2, and @JS1 for their tips.

g(char*s){int y[238]={};while(*s)*y-=--y[*s++];*y/=1;}

Initializes an array of zeros, then uses the ASCII values of the characters in the string as indices to that array to keep track of the number of instances of each character in the string.

Try it online!

• Suggestion: Use variable names that aren't used in keywords, like z,x,c. Jun 19, 2017 at 17:02
• @CalculatorFeline char includes c...
– Neil
Jun 19, 2017 at 17:27
• Also, you only need a 127 element array (\x7f is not printable) and please add an explanation. Jun 19, 2017 at 17:42
• Late to the party, but this should be 96: z;g(char*s){int y[238]={z=0};while(*s)z+=--y[*s++];z/=~0;} Mar 15, 2018 at 10:07
• g(char*s){int y[238]={};while(*s)*y+=--y[*s++];*y/=~0;}
– l4m2
Jan 6, 2019 at 11:07

# JavaScript (ES6), score 81 78

Saved 3 points thanks to @Arnauld

s=>s.replace(d=/./g,z=>q+=d[z]=-~d[z],q=0)&&q

My original score-81 recursive solution:

f=([c,...s],d={})=>c?(d[c]=-~d[c])+f(s,d):0

f l=sum[1|c<-l,d<-c:l,d==c]/2

Try it online!

Anonymizing \l-> gives the same score.

# Jelly, score 6

;\ċ"⁸S

Try it online!

## Retina, score 34

s(O.
M&!^|(?<=(.))\1*
.

Try it online!

### Explanation

s(O.

We start by sorting all the characters in the input so that identical characters are grouped together into a single run. The s( activates singleline mode for all stages (i.e. makes . match linefeeds).

M&!s^|(?<=(.))\1*

The goal is to turn a run of n characters into Tn characters (the nth triangular number) because that's the score of the occurrences of this character. To do so, we find overlapping matches. In particular, for each i in [1,n], we're going to include i-1 characters in the match. We get all those matches due to the overlapping flag &. That gives us n*(n-1)/2 = Tn-1 = Tn - n characters just from the matches. But the match stage will join these with linefeeds, which are n linefeeds for n matches. There's only one problem. There won't be a linefeed after the last match, so the overall number of characters in the output is one less than we need. We fix this by also matching the beginning of the input, which gives us a single leading linefeed if there is at least one other match.

.

Finally, we just count how many characters there are in the string.

f(a:b)=1+sum[1|c<-b,c==a]+f b;f _=0

There's a tab between f and _.

Try it online!

The value of the empty string is 0. The value of the string s, where a is the first char and b the rest of the string is 1 plus the occurrences of a in b plus a recursive call with b.

1#.,@(*+/\"1)&=

Try it online!

## Explanation

1#.,@(*+/\"1)&=
=  Self-classify: bit matrix of equality between input
and its unique elements.
(      )&   Apply verb in parentheses to it:
+/\         running sums
"1       of each row
*            multiplied with original matrix.
This causes the i'th 1 on each row to be replaced by i.
,@            Flatten the resulting matrix
1#.              and interpret as a base-1 number, computing its sum.

Using 1#. instead of +/@ for the sum saved a few points, and & could be used instead of @ in a monadic context to save one more. The repeated 1 costs me one extra point, but I haven't been able to get rid of it.

• "later" waits a quarterday Jun 20, 2017 at 1:56
• @CalculatorFeline 10 hours later is still later. :P Jun 20, 2017 at 6:35
• Let's make it a sesquisemiday now. Jun 20, 2017 at 15:24
• I personally use this format for TIO answers in order to reflect an accurate byte count in the code section, maybe you would want to use it Jun 21, 2017 at 22:29

# R, score: 67 8395128

-61 thanks to top tips from Giuseppe

function(x,y=table(utf8ToInt(x)))y%*%{y+1}/2

Try it online!

The string is split using utf8ToInt and each ascii value is counted table. The the result is calculated using doing a matrix multiplication %*% over that at itself + 1 and finally halfed.

• use table instead of rle; you can get rid of the sort as well (and you don't have to index [[1]] into the result of strsplit) Jun 19, 2017 at 20:36
• @Giuseppe Thanks a lot. I didn't even think of table, will incorporate soon. Jun 19, 2017 at 20:44
• I think you can save a few more bytes by using a different variable name instead of n (since it's in function twice) and also changing (n+1) to {n+1} Jun 19, 2017 at 21:02
• score:67. Some variation on this might make it possible to reduce the score further. Jan 18, 2018 at 17:54
• @Giuseppe ... I should have re read it . whoops Jan 18, 2018 at 18:51

{γ€gLO

Try it online!

# Jelly, score 5

ĠJ€ẎS

Try it online!

Thanks to Leaky Nun for -2 (previously on his answer)

• Ahhh I didn't notice this question fast enough. Jun 19, 2017 at 17:00
• @LeakyNun p.s. you're not always a ninja, nor anyone is Jun 19, 2017 at 17:01
• Really? I don't think so. Jun 19, 2017 at 17:03
• Score 5: ĠJ€ẎS Jun 19, 2017 at 17:30
• @LeakyNun As promised...yeah, the credit is there :) Jun 19, 2017 at 17:36

# Pyth, score 6

1 byte thanks to isaacg.

+F/V._

Test suite.

## How it works

+F/V._
+F/V._QQ  implicit input
/V      vectorize count: for each element in the first argument,
count the number of occurrences of the
second argument:
._Q       all prefixes of input
Q      input
+F        fold (reduce) on +, base case 0.
• s+0 is the same as +F. Jun 19, 2017 at 19:27
• Good! The best I could do is usaShHGrScQ1 8Z for 16. Can you add an explanation? Jun 19, 2017 at 22:27
• @DigitalTrauma I've added an explanation. Jun 20, 2017 at 3:47
• s/LQ is score 4, does this use features that postdates the challenge?
– Dave
Jan 19, 2018 at 3:31

# J, score: 14 12 11

$+/@,2!1#.= Try it online! • Clever use of$.
– cole
Jan 18, 2018 at 20:31
• Nice. Slight 11 byte variation: 1#.2!1+1#.= Apr 28, 2019 at 5:12
• @Jonah Reusing glyphs results in a penalty Apr 28, 2019 at 12:24
• ah, missed that part. Apr 28, 2019 at 14:32

# Jelly, score of 7

ċÐ€QRFS

Explanation:

Q    get unique letters
ċÐ€     count the occurences of each letter in the original string
R   [1..n] for n in list of frequencies
F  flatten list
S sum
(implicit output)

Try it online!

• Welcome to PPCG! Jan 23, 2018 at 1:31

# C, 60 bytes, score 108 95

g(char*s){int y[256]={},z=0;while(*s)z-=--y[*s++];return z;}

Try it online!

Usually pre- and post-increment operators are great for code golf, but they really hurt on this challenge!

EDIT: By subtracting negative counts instead of adding positive ones, I saved a whole bunch of score. Replacing for() with while() eliminated a semicolon as well.

# Perl 6, score  61 56 53 46  44

(1 X..*.comb.Bag.values).flat.sum

Try it

{sum flat 1 X.. .comb.Bag.values}

Try it

{sum flat 1 X.. values(.comb.Bag)}

Try it

{[+] flat	1	X.. values(.comb.Bag)}

Try it

{[+] flat	1	X.. values(bag
.comb)}

Try it

# C# (.NET Core), score ∞ (I mean, 209)

b=>b.Distinct().Select(z=>{var w=b.Count(p=>p==z);return w*(w+1)/2;}).Sum()

Try it online!

The score includes the following:

using System.Linq;
• I know it's been a while, but you can change return w*(w+1)/2 to return-~w*w/2 (score 196). EDIT: You can create a port of my Java 8 answer for a score of 149: using System.Linq;b=>{int[]x=new int[256];return\nb.Select(z=>++x[z]).Sum();} Try it online. Jan 22, 2018 at 13:24
• @KevinCruijssen: I got your solution down to a score of 111: b=>{var x=new int[256];return\nb.Sum(z=>++x[z]);} Mar 15, 2018 at 11:07
• @raznagul (*half year old response incoming*) 109 if you change the second space to a tab. ;) Try it online. Sep 21, 2018 at 15:26
• @KevinCruijssen (another half year old response incoming) 49 with the interactive compiler, and I think it won't ever get below 48. I find it odd how the more golfed C# answers get, the more readable they always seem to become. Try it online! Apr 28, 2019 at 4:47

## PowerShell, score 64

$z=@{}$ARGS|% getE*|%{$u+=($Z.$_+=1)};$U

(Score is based on a single linefeed newline, which isn't Windows standard but does work in PS).

PS C:\> D:\unique-is-cheap.ps1 (gc D:\unique-is-cheap.ps1 -raw)
64
• Hashtable counter @{}
• Iterate through the letters; $args is an array of parameters - in this case the input string makes it a single item array; |% does a foreach loop over the items, and uses the getE* shortcut to match the GetEnumerator() string method and call it to turn the string into a character stream. • |% loop over the characters and increment their hashtable entry, add it to a running total. The ($x+=1) form with parens both modifies the variable and outputs the new value for use.
• Output the running total.

Try it online!

# PHP, 56 bytes, Score 92

FOREAch(COUNT_CHARS($argn)as$z)WHILE($z)$y+=$z--;echo$y;

Try it online!

# Perl 5 score 91 83

Uses the -p flag which adds 2 because of the p in split.

$x=$_;$b+=++$a{$_}for(split//,$x);$_=$b
• Welcome to PPCG! Jun 20, 2017 at 21:22
• Using your answer as a base and applying some techniques from the tips page, I managed to get your score down to 31: Try it online!. $ is automatically printed after each call so we can use that to store the score and /./g` returns a list of all chars in$_, which is cheaper than split//. Jul 17, 2017 at 15:16
• I know this is an old challenge, but you can cut the score even further: Try it online! Mar 13, 2018 at 14:41

# Octave, 39 bytes, Score 69

@(a)sum((b=hist(a,unique(1*a))).^2+b)/2

Try it online!

While there is another Octave answer, this one is entirely my own and a different approach, plus it scores less :).

The approach boils down to first finding the count (b) of each unique character, which is achieved using the histogram function. Then for each element we calculate the sum of 1 to b which is done using the formula (b*(b+1))/2. Then the individual sums are all summed into the final score.

In testing it seems brackets are really costly in the scoring because many are needed. I've optimised down from an initial score of about 88 by rearranging the questions to minimise the number of open/close brackets - hence we now do the /2 on the final total rather than individually, and also I've modified the formula to (b^2+b)/2 as that requires fewer brackets.

• Unfortunately this appears to fail on the empty string: error: hist: subscript indices must be either positive integers less than 2^31 or logicals Jun 21, 2017 at 22:29

# Octave , score 77 31 bytes

*same as @LuisMendo 's MATL answer.

@(a)nnz(triu(a==a'))

Try it online!